All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgroups.util.TimeScheduler3 Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS 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).

There is a newer version: 34.0.0.Final
Show newest version

package org.jgroups.util;


import org.jgroups.Global;
import org.jgroups.logging.Log;
import org.jgroups.logging.LogFactory;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.function.Function;


/**
 * Implementation of {@link TimeScheduler}. Uses a {@link DelayQueue} to order tasks according to execution times
 * @author Bela Ban
 * @since  3.3
 */
public class TimeScheduler3 implements TimeScheduler, Runnable {
    /** Thread pool used to execute the tasks */
    protected Executor                    pool;
    protected ThreadPool                  thread_pool;

    /** DelayQueue with tasks being sorted according to execution times (next execution first) */
    protected final BlockingQueue   queue=new DelayQueue<>();

    /** Thread which removes tasks ready to be executed from the queue and submits them to the pool for execution */
    protected volatile Thread             runner;

    protected static final Log            log=LogFactory.getLog(TimeScheduler3.class);

    protected ThreadFactory               timer_thread_factory;

    // if true, non-blocking timer tasks are run directly by the runner thread and not submitted to the thread pool
    protected boolean                     non_blocking_task_handling=true;

    protected boolean                     shut_down_pool;

    protected enum TaskType               {dynamic, fixed_rate, fixed_delay}


    /**
     * Create a scheduler that executes tasks in dynamically adjustable intervals
     */
    public TimeScheduler3() {
        pool=new ThreadPoolExecutor(4, 10,
                                    30000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100),
                                    Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
        shut_down_pool=true;
        start();
    }


    public TimeScheduler3(ThreadFactory factory, int min_threads, int max_threads, long keep_alive_time, int max_queue_size,
                          String rejection_policy) {
        this(factory, min_threads, max_threads, keep_alive_time, new ArrayBlockingQueue<>(max_queue_size), rejection_policy, true);
    }

    public TimeScheduler3(ThreadFactory factory, int min_threads, int max_threads, long keep_alive_time,
                          BlockingQueue queue, String rejection_policy, boolean thread_pool_enabled) {
        timer_thread_factory=factory;
        pool=thread_pool_enabled?
          new ThreadPoolExecutor(min_threads, max_threads,keep_alive_time, TimeUnit.MILLISECONDS,
                                 queue, factory, Util.parseRejectionPolicy(rejection_policy))
          : new DirectExecutor();
        shut_down_pool=true;
        start();
    }

    public TimeScheduler3(ThreadPool thread_pool, ThreadFactory factory, boolean start) {
        timer_thread_factory=factory;
        this.thread_pool=Objects.requireNonNull(thread_pool);
        pool=thread_pool.getThreadPool();
        if(start)
            start();
    }

    public void    setThreadFactory(ThreadFactory f)     {condSet((p) -> p.setThreadFactory(f));}
    public void    setThreadPool(Executor new_pool)      {pool=new_pool;}
    public int     getMinThreads()                       {return condGet(ThreadPoolExecutor::getCorePoolSize, 0);}
    public void    setMinThreads(int size)               {condSet(p -> p.setCorePoolSize(size));}
    public int     getMaxThreads()                       {return condGet(ThreadPoolExecutor::getMaximumPoolSize, 0);}
    public void    setMaxThreads(int size)               {condSet(p -> p.setMaximumPoolSize(size));}
    public long    getKeepAliveTime()                    {return condGet(p -> p.getKeepAliveTime(TimeUnit.MILLISECONDS), 0L);}
    public void    setKeepAliveTime(long time)           {condSet(p -> p.setKeepAliveTime(time, TimeUnit.MILLISECONDS));}
    public int     getCurrentThreads()                   {return condGet(ThreadPoolExecutor::getPoolSize, 0);}
    public int     getQueueSize()                        {return condGet(p -> p.getQueue().size(), 0);}
    public int     size()                                {return queue.size();}
    public String  toString()                            {return getClass().getSimpleName();}
    public boolean isShutdown()                          {return condGet(ThreadPoolExecutor::isShutdown, false);}
    public boolean getNonBlockingTaskHandling()          {return non_blocking_task_handling;}
    public void    setNonBlockingTaskHandling(boolean b) {this.non_blocking_task_handling=b;}


    public String dumpTimerTasks() {
        StringBuilder sb=new StringBuilder();
        for(Task task: queue) {
            sb.append(task);
            if(task.isCancelled())
                sb.append(" (cancelled)");
            sb.append("\n");
        }
        return sb.toString();
    }

    public void removeCancelledTasks() {
        queue.removeIf(Task::isDone);
    }


    public void execute(Runnable task, boolean can_block) {
        submitToPool(task instanceof TimeScheduler.Task?
                       new RecurringTask(task, TaskType.dynamic, 0, ((TimeScheduler.Task)task).nextInterval(), TimeUnit.MILLISECONDS, can_block)
                       : new Task(task, can_block)); // we'll execute the task directly
    }


    public Future schedule(Runnable work, long initial_delay, TimeUnit unit, boolean can_block) {
        return doSchedule(new Task(work, initial_delay, unit, can_block), initial_delay);
    }



    public Future scheduleWithFixedDelay(Runnable work, long initial_delay, long delay, TimeUnit unit, boolean can_block) {
        return scheduleRecurring(work, TaskType.fixed_delay, initial_delay, delay, unit, can_block);
    }


    public Future scheduleAtFixedRate(Runnable work, long initial_delay, long delay, TimeUnit unit, boolean can_block) {
        return scheduleRecurring(work,TaskType.fixed_rate,initial_delay,delay,unit, can_block);
    }


    /**
     * Schedule a task for execution at varying intervals. After execution, the task will get rescheduled after
     * {@link org.jgroups.util.TimeScheduler.Task#nextInterval()} milliseconds. The task is never done until
     * nextInterval() returns a value <= 0 or the task is cancelled.

* Note that the task is rescheduled relative to the last time is actually executed. This is similar to * {@link #scheduleWithFixedDelay(Runnable,long,long,java.util.concurrent.TimeUnit)}. * @param work the task to execute */ public Future scheduleWithDynamicInterval(TimeScheduler.Task work, boolean can_block) { return scheduleRecurring(work, TaskType.dynamic, work.nextInterval(), 0, TimeUnit.MILLISECONDS, can_block); } public synchronized void start() { if(runner == null || !runner.isAlive()) { runner=timer_thread_factory != null? timer_thread_factory.newThread(this, "Timer runner") : new Thread(this, "Timer runner"); runner.start(); } } /** Stops the timer, cancelling all tasks */ public synchronized void stop() { Thread tmp=runner; runner=null; if(tmp != null) { tmp.interrupt(); try {tmp.join(500);} catch(InterruptedException e) {} } // we may need to do multiple iterations as the iterator works on a copy and tasks might have been added just // after the iterator() call returned while(!queue.isEmpty()) { for(Task entry : queue) { entry.cancel(true); queue.remove(entry); } } if(pool instanceof ThreadPoolExecutor && shut_down_pool) { ThreadPoolExecutor p=(ThreadPoolExecutor)pool; List remaining_tasks=p.shutdownNow(); remaining_tasks.stream().filter(task -> task instanceof Future).forEach(task -> ((Future)task).cancel(true)); p.getQueue().clear(); try { p.awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS); } catch(InterruptedException e) { } } // clears the threads list (https://issues.redhat.com/browse/JGRP-1971) if(timer_thread_factory instanceof LazyThreadFactory) ((LazyThreadFactory)timer_thread_factory).destroy(); } public void run() { while(Thread.currentThread() == runner) { try { Task task=queue.take(); if(!task.isDone()) submitToPool(task); } catch(InterruptedException interrupted) { // flag is cleared and we check if the loop should be terminated at the top of the loop } catch(Throwable t) { log.error(Util.getMessage("FailedSubmittingTaskToThreadPool"), t); } } } protected Future scheduleRecurring(Runnable work, TaskType type, long initial_delay, long delay, TimeUnit unit, boolean can_block) { return doSchedule(new RecurringTask(work, type, initial_delay, delay, unit, can_block), initial_delay); } protected Future doSchedule(Task task, long initial_delay) { if(task.getRunnable() == null) throw new NullPointerException(); if (isShutdown()) return null; if(initial_delay <= 0) { submitToPool(task); return task; } return add(task); } protected void condSet(Consumer setter) { if(pool instanceof ThreadPoolExecutor) setter.accept((ThreadPoolExecutor)pool); } protected T condGet(Function getter, T default_value) { if(pool instanceof ThreadPoolExecutor) return getter.apply((ThreadPoolExecutor)pool); return default_value; } protected void submitToPool(Task task) { if(non_blocking_task_handling && !task.canBlock()) { task.run(); return; } try { if(pool == null) { if((pool=thread_pool.getThreadPool()) == null) { log.warn("timer: thread pool is null, will use caller's thread to execute task %s", task); task.run(); return; } } 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(); } } protected Task add(Task task) { queue.add(task); // removeCancelledTasks(); return task; } protected boolean isRunning() { Thread tmp=runner; return tmp != null && tmp.isAlive(); } public static class Task implements Runnable, Delayed, Future { protected final Runnable runnable; // the task to execute protected long creation_time; // time (in ns) at which the task was created protected long delay; // time (in ns) after which the task should execute protected volatile boolean cancelled; protected volatile boolean done; protected final boolean can_block; public Task(Runnable runnable, boolean can_block) { this.runnable=runnable; this.can_block=can_block; } public Task(Runnable runnable, long initial_delay, TimeUnit unit, boolean can_block) { this.can_block=can_block; this.creation_time=System.nanoTime(); this.delay=TimeUnit.NANOSECONDS.convert(initial_delay, unit); this.runnable=runnable; if(runnable == null) throw new IllegalArgumentException("runnable cannot be null"); } public Runnable getRunnable() {return runnable;} public boolean canBlock() {return can_block;} public int compareTo(Delayed o) { long my_delay=getDelay(TimeUnit.NANOSECONDS), other_delay=o.getDelay(TimeUnit.NANOSECONDS); // return Long.compare(my_delay, other_delay); // JDK 7 only return Long.compare(my_delay, other_delay); } public long getDelay(TimeUnit unit) { // time (in ns) until execution, can be negative when already elapsed long remaining_time=delay - (System.nanoTime() - creation_time); return unit.convert(remaining_time, TimeUnit.NANOSECONDS); } 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 { runnable.run(); } catch(Throwable t) { log.error(Util.getMessage("FailedExecutingTask") + ' ' + runnable, t); } finally { done=true; } } public String toString() { return String.format("%s (can block=%b)", runnable.toString(), can_block); } } /** Tasks which runs more than once, either dynamic, fixed-rate or fixed-delay, until cancelled */ protected class RecurringTask extends Task { protected final TaskType type; protected final long period; // ns protected final long initial_delay; // ns protected int cnt=1; // number of invocations (for fixed rate invocations) public RecurringTask(Runnable runnable, TaskType type, long initial_delay, long delay, TimeUnit unit, boolean can_block) { super(runnable, initial_delay, unit, can_block); this.initial_delay=TimeUnit.NANOSECONDS.convert(initial_delay, TimeUnit.MILLISECONDS); this.type=type; period=TimeUnit.NANOSECONDS.convert(delay, unit); if(type == TaskType.dynamic && !(runnable instanceof TimeScheduler.Task)) throw new IllegalArgumentException("Need to provide a TimeScheduler.Task as runnable when type is dynamic"); } public void run() { if(isDone()) return; super.run(); if(cancelled) return; done=false; // run again switch(type) { case dynamic: long next_interval=TimeUnit.NANOSECONDS.convert(((TimeScheduler.Task)runnable).nextInterval(), TimeUnit.MILLISECONDS); if(next_interval <= 0) { if(log.isTraceEnabled()) log.trace("task will not get rescheduled as interval is " + next_interval); done=true; return; } creation_time=System.nanoTime(); delay=next_interval; break; case fixed_rate: delay=initial_delay + cnt++ * period; break; case fixed_delay: creation_time=System.nanoTime(); delay=period; break; } add(this); // schedule this task again } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy