org.jgroups.util.TimeScheduler2 Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including
all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and
Jakarta Messaging BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
package org.jgroups.util;
import org.jgroups.Global;
import org.jgroups.annotations.GuardedBy;
import org.jgroups.logging.Log;
import org.jgroups.logging.LogFactory;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Implementation of {@link org.jgroups.util.TimeScheduler}. Uses a thread pool and a single thread which waits for the
* next task to be executed. When ready, it passes the task to the associated pool to get executed. When multiple tasks
* are scheduled to get executed at the same time, they're collected in a queue associated with the task execution
* time, and are executed together.
*
* @author Bela Ban
* @deprecated Use {@link org.jgroups.util.TimeScheduler3} instead
*/
@Deprecated
public class TimeScheduler2 implements TimeScheduler, Runnable {
private final ThreadPoolExecutor pool;
private final ConcurrentSkipListMap tasks=new ConcurrentSkipListMap<>();
private Thread runner=null;
private final Lock lock=new ReentrantLock();
private final Condition tasks_available=lock.newCondition();
@GuardedBy("lock")
private long next_execution_time=0;
/** Needed to signal going from 0 tasks to non-zero (we cannot use tasks.isEmpty() here ...) */
protected final AtomicBoolean no_tasks=new AtomicBoolean(true);
protected volatile boolean running=false;
protected static final Log log=LogFactory.getLog(TimeScheduler2.class);
protected ThreadFactory timer_thread_factory=null;
protected static final long SLEEP_TIME=10000;
/**
* Create a scheduler that executes tasks in dynamically adjustable intervals
*/
public TimeScheduler2() {
pool=new ThreadPoolExecutor(4, 10,
5000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(5000),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
init();
}
public TimeScheduler2(ThreadFactory factory, int min_threads, int max_threads, long keep_alive_time, int max_queue_size,
String rejection_policy) {
timer_thread_factory=factory;
RejectedExecutionHandler tmp=Util.parseRejectionPolicy(rejection_policy);
pool=new ThreadPoolExecutor(min_threads, max_threads,keep_alive_time, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue(max_queue_size),
factory, tmp);
init();
}
public TimeScheduler2(int corePoolSize) {
pool=new ThreadPoolExecutor(corePoolSize, corePoolSize * 2,
5000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(5000),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
init();
}
public void setThreadFactory(ThreadFactory f) {pool.setThreadFactory(f);}
public int getMinThreads() {return pool.getCorePoolSize();}
public void setMinThreads(int size) {pool.setCorePoolSize(size);}
public int getMaxThreads() {return pool.getMaximumPoolSize();}
public void setMaxThreads(int size) {pool.setMaximumPoolSize(size);}
public long getKeepAliveTime() {return pool.getKeepAliveTime(TimeUnit.MILLISECONDS);}
public void setKeepAliveTime(long time) {pool.setKeepAliveTime(time, TimeUnit.MILLISECONDS);}
public int getCurrentThreads() {return pool.getPoolSize();}
public int getQueueSize() {return pool.getQueue().size();}
public String dumpTimerTasks() {
StringBuilder sb=new StringBuilder();
for(Entry entry: tasks.values()) {
sb.append(entry.dump()).append("\n");
}
return sb.toString();
}
public void execute(Runnable task) {
schedule(task, 0, TimeUnit.MILLISECONDS);
}
public Future> schedule(Runnable work, long delay, TimeUnit unit) {
if(work == null)
return null;
Future> retval=null;
long key=System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(delay, unit); // execution time
Entry task=new Entry(work);
while(!isShutdown()) {
Entry existing=tasks.putIfAbsent(key, task);
if(existing == null) {
retval=task.getFuture();
break; // break out of the while loop
}
if((retval=existing.add(work)) != null)
break;
// Else the existing entry is completed. It'll be removed shortly, so we just loop around again.
// Don't remove the entry ourselves - see JGRP-1457.
}
if(key < next_execution_time || no_tasks.compareAndSet(true, false)) {
if(key >= next_execution_time)
key=0L;
taskReady(key);
}
return retval;
}
public Future> scheduleWithFixedDelay(Runnable task, long initial_delay, long delay, TimeUnit unit) {
if(task == null)
throw new NullPointerException();
if (isShutdown())
return null;
RecurringTask wrapper=new FixedIntervalTask(task, delay);
wrapper.doSchedule(initial_delay);
return wrapper;
}
public Future> scheduleAtFixedRate(Runnable task, long initial_delay, long delay, TimeUnit unit) {
if(task == null)
throw new NullPointerException();
if (isShutdown())
return null;
RecurringTask wrapper=new FixedRateTask(task, delay);
wrapper.doSchedule(initial_delay);
return wrapper;
}
/**
* Schedule a task for execution at varying intervals. After execution, the task will get rescheduled after
* {@link org.jgroups.util.TimeScheduler2.Task#nextInterval()} milliseconds. The task is neve done until nextInterval()
* return a value <= 0 or the task is cancelled.
* @param task the task to execute
* Task is rescheduled relative to the last time it actually started execution
* false:
Task is scheduled relative to its last execution schedule. This has the effect
* that the time between two consecutive executions of the task remains the same.
* Note that relative is always true; we always schedule the next execution relative to the last *actual*
*/
public Future> scheduleWithDynamicInterval(Task task) {
if(task == null)
throw new NullPointerException();
if (isShutdown())
return null;
RecurringTask task_wrapper=new DynamicIntervalTask(task);
task_wrapper.doSchedule(); // calls schedule() in ScheduledThreadPoolExecutor
return task_wrapper;
}
/**
* Returns the number of tasks currently in the timer
* @return The number of tasks currently in the timer
*/
public int size() {
int retval=0;
Collection values=tasks.values();
for(Entry entry: values)
retval+=entry.size();
return retval;
}
public String toString() {
return getClass().getSimpleName();
}
/**
* Stops the timer, cancelling all tasks
*
* @throws InterruptedException if interrupted while waiting for thread to return
*/
public void stop() {
stopRunner();
java.util.List remaining_tasks=pool.shutdownNow();
for(Runnable task: remaining_tasks) {
if(task instanceof Future) {
Future future=(Future)task;
future.cancel(true);
}
}
pool.getQueue().clear();
try {
pool.awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS);
}
catch(InterruptedException e) {
}
for(Entry entry: tasks.values())
entry.cancel();
tasks.clear();
}
public boolean isShutdown() {
return pool.isShutdown();
}
public void run() {
while(running) {
try {
_run();
}
catch(Throwable t) {
log.error("failed executing tasks(s)", t);
}
}
}
protected void _run() {
ConcurrentNavigableMap head_map; // head_map = entries which are <= curr time (ready to be executed)
if(!(head_map=tasks.headMap(System.currentTimeMillis(), true)).isEmpty()) {
final List keys=new LinkedList<>();
for(Map.Entry entry: head_map.entrySet()) {
final Long key=entry.getKey();
final Entry val=entry.getValue();
Runnable task=new Runnable() {public void run() {val.execute();}};
try {
pool.execute(task);
}
catch(RejectedExecutionException rejected) { // only thrown if rejection policy is "abort"
Thread thread=timer_thread_factory != null?
timer_thread_factory.newThread(task, "Timer temp thread")
: new Thread(task, "Timer temp thread");
thread.start();
}
keys.add(key);
}
// we cannot use headMap.clear(); removed performance hotspot (https://issues.jboss.org/browse/JGRP-1490)
for(Long key: keys)
tasks.remove(key);
}
if(tasks.isEmpty()) {
no_tasks.compareAndSet(false, true);
waitFor(); // sleeps until time elapses, or a task with a lower execution time is added
}
else
waitUntilNextExecution(); // waits until next execution, or a task with a lower execution time is added
}
protected void init() {
startRunner();
}
/**
* Sleeps until the next task in line is ready to be executed
*/
protected void waitUntilNextExecution() {
lock.lock();
try {
if(!running)
return;
next_execution_time=tasks.firstKey();
long sleep_time=next_execution_time - System.currentTimeMillis();
tasks_available.await(sleep_time, TimeUnit.MILLISECONDS);
}
catch(InterruptedException e) {
}
finally {
lock.unlock();
}
}
protected void waitFor() {
lock.lock();
try {
if(!running)
return;
tasks_available.await(SLEEP_TIME, TimeUnit.MILLISECONDS);
}
catch(InterruptedException e) {
}
finally {
lock.unlock();
}
}
/**
* Signals that a task with a lower execution time than next_execution_time is ready
*/
protected void taskReady(long trigger_time) {
lock.lock();
try {
if(trigger_time > 0)
next_execution_time=trigger_time;
tasks_available.signal();
}
finally {
lock.unlock();
}
}
protected void startRunner() {
running=true;
runner=timer_thread_factory != null? timer_thread_factory.newThread(this, "Timer runner") : new Thread(this, "Timer runner");
runner.start();
}
protected void stopRunner() {
lock.lock();
try {
running=false;
tasks_available.signal();
}
finally {
lock.unlock();
}
}
private static class Entry {
private final MyTask task; // the task (wrapper) to execute
private MyTask last; // points to the last task
private final Lock lock=new ReentrantLock();
@GuardedBy("lock")
private boolean completed=false; // set to true when the task has been executed
private Entry(Runnable task) {
last=this.task=new MyTask(task);
}
Future> getFuture() {
return task;
}
Future> add(Runnable task) {
lock.lock();
try {
if(completed)
return null;
MyTask retval=new MyTask(task);
last.next=retval;
last=last.next;
return retval;
}
finally {
lock.unlock();
}
}
void execute() {
lock.lock();
try {
if(completed)
return;
completed=true;
for(MyTask tmp=task; tmp != null; tmp=tmp.next) {
if(!(tmp.isCancelled() || tmp.isDone())) {
try {
tmp.run();
}
catch(Throwable t) {
log.error("task execution failed", t);
}
finally {
tmp.done=true;
}
}
}
}
finally {
lock.unlock();
}
}
void cancel() {
lock.lock();
try {
if(completed)
return;
for(MyTask tmp=task; tmp != null; tmp=tmp.next)
tmp.cancel(true);
}
finally {
lock.unlock();
}
}
int size() {
int retval=1;
for(MyTask tmp=task.next; tmp != null; tmp=tmp.next)
retval++;
return retval;
}
public String toString() {
return size() + " tasks";
}
public String dump() {
StringBuilder sb=new StringBuilder();
boolean first=true;
for(MyTask tmp=task; tmp != null; tmp=tmp.next) {
if(!first)
sb.append(", ");
else
first=false;
sb.append(tmp);
}
return sb.toString();
}
}
/**
* Simple task wrapper, always executed by at most 1 thread.
*/
protected static class MyTask implements Future, Runnable {
protected final Runnable task;
protected volatile boolean cancelled=false;
protected volatile boolean done=false;
protected MyTask next;
protected MyTask(Runnable task) {
this.task=task;
}
public boolean cancel(boolean mayInterruptIfRunning) {
boolean retval=!isDone();
cancelled=true;
return retval;
}
public boolean isCancelled() {
return cancelled;
}
public boolean isDone() {
return done || cancelled;
}
public Object get() throws InterruptedException, ExecutionException {
return null;
}
public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return null;
}
public void run() {
if(isDone())
return;
try {
task.run();
}
catch(Throwable t) {
log.error("failed executing task " + task, t);
}
finally {
done=true;
}
}
public String toString() {
return task.toString();
}
}
/**
* Task which executes multiple times. An instance of this class wraps the real task and intercepts run(): when
* called, it forwards the call to task.run() and then schedules another execution (until cancelled). The
* {@link #nextInterval()} method determines the time to wait until the next execution.
* @param
*/
private abstract class RecurringTask implements Runnable, Future {
protected final Runnable task;
protected volatile Future> future; // cannot be null !
protected volatile boolean cancelled=false;
private RecurringTask(Runnable task) {
this.task=task;
}
/**
* The time to wait until the next execution
* @return Number of milliseconds to wait until the next execution is scheduled
*/
protected abstract long nextInterval();
protected boolean rescheduleOnZeroDelay() {return false;}
public void doSchedule() {
long next_interval=nextInterval();
if(next_interval <= 0 && !rescheduleOnZeroDelay()) {
if(log.isTraceEnabled())
log.trace("task will not get rescheduled as interval is " + next_interval);
return;
}
future=schedule(this, next_interval, TimeUnit.MILLISECONDS);
if(cancelled)
future.cancel(true);
}
public void doSchedule(long next_interval) {
future=schedule(this, next_interval, TimeUnit.MILLISECONDS);
if(cancelled)
future.cancel(true);
}
public void run() {
if(cancelled) {
if(future != null)
future.cancel(true);
return;
}
try {
task.run();
}
catch(Throwable t) {
log.error("failed running task " + task, t);
}
if(!cancelled)
doSchedule();
}
public boolean cancel(boolean mayInterruptIfRunning) {
boolean retval=!isDone();
cancelled=true;
if(future != null)
future.cancel(mayInterruptIfRunning);
return retval;
}
public boolean isCancelled() {
return cancelled;
}
public boolean isDone() {
return cancelled || (future == null || future.isDone());
}
public V get() throws InterruptedException, ExecutionException {
return null;
}
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return null;
}
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append(task + ", cancelled=" + isCancelled());
return sb.toString();
}
}
private class FixedIntervalTask extends RecurringTask {
final long interval;
private FixedIntervalTask(Runnable task, long interval) {
super(task);
this.interval=interval;
}
protected long nextInterval() {
return interval;
}
}
private class FixedRateTask extends RecurringTask {
final long interval;
final long first_execution;
int num_executions=0;
private FixedRateTask(Runnable task, long interval) {
super(task);
this.interval=interval;
this.first_execution=System.currentTimeMillis();
}
protected long nextInterval() {
long target_time=first_execution + (interval * ++num_executions);
return target_time - System.currentTimeMillis();
}
protected boolean rescheduleOnZeroDelay() {return true;}
}
private class DynamicIntervalTask extends RecurringTask {
private DynamicIntervalTask(Task task) {
super(task);
}
protected long nextInterval() {
if(task instanceof Task)
return ((Task)task).nextInterval();
return 0;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy