Simple use of ScheduledExecutorService deferred thread pool

Keywords: Java thread pool



1, Timer

There is a Timer class under the java.util package, which is used to implement scheduled tasks

1. Code test

Code implementation steps:

1. Create a timer object

2. Call the schedule polymorphic method of timer object and select the way to execute the task according to the different incoming parameters

  • The first test is a one-time task
  • The second test is a periodically executed task
public class TimerTest {
	private static Logger logger = LoggerFactory.getLogger(TimerTest.class);
	public static void main(String[] args) {
		Timer timer = new Timer();
		timer.schedule(new TimerTask() {
			@Override
			public void run() {
				logger.info("Thread startup 2 s Post execution");
			}
		}, 2000);

		timer.scheduleAtFixedRate(new TimerTask() {
			@Override
			public void run() {
				logger.info("Project start 1 s After execution, the execution cycle is 2 s");
			}
		}, 1000, 2000);
	}
}



Test results:




2. Summary

When we instantiate the object of Timer class, it will create a thread for later task execution. This is a major feature of using the Timer class to perform scheduled tasks. If there is a thread exception in our scenario, but other tasks can continue to execute other tasks, the use of Timer must not meet the requirements.



After JDK1.5, Doug Lea introduced ScheduledExecutorService under the Java concurrency package. It has the feature of reusing threads in the thread pool and supports multithreading. If one thread hangs, it will not affect the operation of other threads (the thread with exception will be discarded by the waiters queue of the thread pool, that is, it will become a thread with null task). For details on how thread pools are discarded, please refer to: On the underlying source code of ThreadPoolExecutor thread pool





2, ScheduledExecutorService

According to the class diagram, ScheduledExecutorService is a subclass of ThreadPoolExecutor, which has the characteristics of thread pool



1. Simple use

public class ScheduledThreadPoolTest {
	private static Logger logger = LoggerFactory.getLogger(ScheduledThreadPoolTest.class);

	public static void main(String[] args) throws ExecutionException, InterruptedException {

		ScheduledExecutorService scheduled = new ScheduledThreadPoolExecutor(1);
		// callable interface of schedule method
		ScheduledFuture<String> ret = scheduled.schedule(new Callable<String>() {
			@Override
			public String call() throws Exception {
				return "callable Interface return";
			}
		}, 1, TimeUnit.SECONDS);
		logger.info(ret.get());


		// runable interface of schedule method
		scheduled.schedule(new Runnable() {
			@Override
			public void run() {
				logger.info("1 after thread startup s Re execution");

			}
		}, 1, TimeUnit.SECONDS);

		
		// It is executed periodically, and those that are too late to consume will be stored in the queue
		scheduled.scheduleAtFixedRate(() -> {
			// A task is generated every 2s. If the task is too late to consume, it will be stored in the corresponding queue
			logger.info("At thread start 1 s After execution, and the time of subsequent cycle execution is 2 s");
			try {
				// The thread execution time is 3s: 3 < 2, so the next task will be performed immediately after the task is executed
				TimeUnit.SECONDS.sleep(3);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}, 1, 2, TimeUnit.SECONDS);

		// Periodic execution, too late for consumption, and wait for consumption to be completed
		scheduled.scheduleWithFixedDelay(() -> {
			try {
				// The task interval is 5s: business time + cycle time
				TimeUnit.SECONDS.sleep(3);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			logger.info("1 after thread startup s Then execute, and the subsequent execution cycle is 2 s. Note: this 2 s Is 2 after the previous task is completed s,It does not pile up in the queue");
		}, 1, 2, TimeUnit.SECONDS);
	}
}



The four test cases correspond to the four API s in the ScheduledExecutorService interface. The corresponding effect is written in the comment log of the above code




2. Source code analysis

And thread pools are analyzed in roughly the same order

1. The scheduleAtFixedRate method is used as the entry (the other three methods are similar, but different methods set different parameters for later condition judgment). Note that sft.outerTask = t here, which will appear again later



2. Call the corresponding delayedExecute method



3. The step of adding tasks to the queue has been completed. Next, when the delay conditions are met, the delay queue will pop up the corresponding thread task and call the corresponding run method



4. Call the run method

  1. Determine whether it is periodic or one-time: the judgment result here depends on which API (four methods in the interface) we call
  2. Set the next execution time: there are two API s for periodic tasks, one is scheduleAtFixedRate and the other is scheduleWithFixedDelay. The values passed in by the two methods are positive, but the latter method will encapsulate the passed in value as a negative number (the value of the previous method remains unchanged). The value here is determined according to the result of this encapsulation
  3. Add the task to be executed next time to the queue: the task to be executed next time is sft.outerTask = t set in step 1, that is, add yourself to the queue again



5. The next execution of a recurring task,



Add: the implementation of delayed thread pool is not difficult to understand. The DelayQueue queue is internally maintained, and the priority queue is used in the DelayQueue queue. Its internal implementation is based on the binary heap data structure (for those who are not familiar with the common queues under concurrent packages, please refer to my previous blog: Simple learning of seven common queues , the concept of these queues must be understood clearly, which is very important for you to learn the whole concurrency knowledge). In addition, the elements inside DelayQueue can be sorted according to rules based on the Comparable interface, that is, the effect of out of the queue in the specified order is achieved.

// Construction method of delay queue
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}



At present, there are many task scheduling components. In addition to the Timer and scheduled executorservice mentioned above, there are also spring tasks, Quartz, XXL job, elastic job, etc. But I think learning JDK's own components well is the basis for studying other components!!!

Posted by bender on Sun, 05 Dec 2021 13:15:30 -0800