Recently, I encountered a new requirement in the project, which is to implement a function that can dynamically add timed tasks. Speaking of this, some people may say it is simple, just use quartz, it is simple and crude. However, the quartz framework is too heavy, and small projects are not easy to operate. Of course, some people may say that jdk provides timer interface, which is completely sufficient. However, the requirements of our project are completely multi-threaded models, while the timer is single-threaded, so, and the author finally chose the thread pool of jdk.
What is a thread pool
Java provides four types of thread pools through Executors, namely:
newCachedThreadPool: Create a cacheable thread pool. If the thread pool length exceeds the processing needs, you can flexibly recycle idle threads. If there is no recycle, create a new thread.
newFixedThreadPool: Creates a fixed-length thread pool that can control the maximum number of threads concurrency, and the excess threads will wait in the queue.
newScheduledThreadPool: Creates a fixed-length thread pool that supports timed and periodic task execution.
newSingleThreadExecutor: Creates a single-threaded thread pool, which will only use a unique worker thread to execute tasks, ensuring that all tasks are executed in the specified order (FIFO, LIFO, priority).
The poster uses newScheduledThreadPool in the project. That’s all. No matter how many poster you are, you will show off your skills. Google it and it’s a lot.
Getting thread pool service
The author uses singleton mode to obtain the service of the thread pool. The code is as follows:
/** * Thread pool creation. * @author wuhf * @date 2018/01/16 */public class ThreadPoolUtils { private static ScheduledExecutorService executorService; private ThreadPoolUtils() { //Manually create thread pool. executorService = new ScheduledThreadPoolExecutor(10, new BasicThreadFactory.Builder().namingPattern("syncdata-schedule-pool-%d").daemon(true).build()); } private static class PluginConfigHolder { private final static ThreadPoolUtils INSTANCE = new ThreadPoolUtils(); } public static ThreadPoolUtils getInstance() { return PluginConfigHolder.INSTANCE; } public ScheduledExecutorService getThreadPool(){ return executorService; }}Interrupting a running thread code implementation
I won't say much nonsense, the code is as follows:
/** * A task in the thread pool is interrupted. */public class InterruptThread implements Runnable { private int num; public InterruptThread (int num){ this.num = num; } public static void main(String[] args) throws InterruptedException { Thread interruptThread = new Thread(new InterruptThread(1)); ScheduledFuture<?> t = ThreadPoolUtils.getInstance().getThreadPool().scheduleAtFixedRate(interruptThread,0,2, TimeUnit.SECONDS); InterruptThread interruptThread1 = new InterruptThread(2); ThreadPoolUtils.getInstance().getThreadPool().scheduleAtFixedRate(interruptThread1,0,2, TimeUnit.SECONDS); InterruptThread interruptThread2 = new InterruptThread(3); ThreadPoolUtils.getInstance().getThreadPool().scheduleAtFixedRate(interruptThread2,0,2, TimeUnit.SECONDS); Thread.sleep(5000);// Terminate running thread interruptThread t.cancel(true); while (true){ } } @Override public void run() { System.out.println("this is a thread" + num); }}Trapped record
When the poster was using the following code, he suddenly thought about how to stop the thread from running when this timing task needs to be stopped.
ThreadPoolUtils.getInstance().getThreadPool().scheduleAtFixedRate(interruptThread,0,2, TimeUnit.SECONDS);
Since I have such a need, let’s google it. After searching for most of the time, I couldn’t find any relevant information. They were all in-depth analysis of Java thread pool. Or global variables or something, no solution was found to satisfy the author.
Since there is no thread, let’s take a look at the underlying source code of scheduleAtFixedRate to see what it is. As expected, I saw the specific implementation of scheduleAtFixedRate method in the source code and found that its return value is ScheduledFuture.
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); if (period <= 0) throw new IllegalArgumentException(); ScheduledFutureTask<Void> sft = new ScheduledFutureTask<Void>(command, null, triggerTime(initialDelay, unit), unit.toNanos(period)); RunnableScheduledFuture<Void> t = decorateTask(command, sft); sft.outerTask = t; delayedExecute(t); return t; }Then let's take a look at what's inside ScheduledFuture. It didn't disappoint the poster, and I saw this
public boolean cancel(boolean mayInterruptIfRunning) { boolean cancelled = super.cancel(mayInterruptIfRunning); if (cancelled && removeOnCancel && heapIndex >= 0) remove(this); return cancelled;} //Remove the current thread from the thread's running queue public boolean remove(Runnable task) { boolean removed = workQueue.remove(task); tryTerminate(); // In case SHUTDOWN and now empty return removed;}Let's check what super.cancel(mayInterruptIfRunning) is. We see this.
//Stop the thread running by calling the interrupt method of the thread public boolean cancel(boolean mayInterruptIfRunning) { if (!(state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED))) return false; try { // in case call to interrupt throws exception if (mayInterruptIfRunning) { try { Thread t = runner; if (t != null) t.interrupt(); } finally { // final state UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); } } } finally { finishCompletion(); } return true; }All the problems here are solved.
Let's summarize
There are always difficult solutions in projects. When Google is not easy to find, it may be a good way to search for jdk's source code.