preface
Thread pool series:
The thread return value of a Java thread pool
This article will focus on how to use thread pools and the use of some common APIS of thread pools. Through this article, you will learn:
Thread pool status 2. Common API of thread pool 3. How to enable thread pool
1. Thread pool status
The thread pool has five states, which are:
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
Copy the code
RUNNING– > RUNNING status: indicates that the thread pool is RUNNING properly. SHUTDOWN– > Closed state: the thread pool is being closed. STOP– > STOP state, indicating that the thread pool is being stopped. TIDYING– > TIDYING state, indicating that the thread pool is being TIDYING. TERMINATED state, indicating that the thread pool is TERMINATED.
The five conversions are as follows:
Among them:
Shutdown () stops idle threads. ShutdownNow () stops all threads and empties the wait queue. TryTerminate () determines whether the current state satisfies the flow to TIDYING state. Terminated () is an empty method that subclasses can implement to clean up the business layer.
The thread pool state is irreversible and runs from RUNNING to TERMINATED. The thread pool was created in the RUNNING state. New tasks are accepted only when you are in RUNNING state. In the SHUTDOWN state, tasks in the waiting queue can still be executed. In the STOP state, the wait queue is cleared and all threads are terminated. TIDYING: There are no threads in the thread pool. In TERMINATED state, the thread pool has stopped completely.
Common THREAD pool apis
Once you have constructed a ThreadPoolExecutor object, you can use the exposed methods it provides. Here are a few commonly used methods.
A constructor
There are four constructors, all of which end up calling:
/ * * *@paramCorePoolSize Number of core threads *@paramMaximumPoolSize Maximum number of threads *@paramKeepAliveTime Specifies the maximum idle time for a thread, after which it is reclaimed@paramUnit keepAliveTime unit *@paramWorkQueue Wait queue *@paramThreadFactory specifies the threadFactory@paramHandler rejects the policy */
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
// Record to a member variable
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
Copy the code
Other methods
Public void allowCoreThreadTimeOut(Boolean value) Specifies whether the core thread is allowed to be idle for a certain period of time before being recycled. Value =true indicates that the core thread is allowed to be idle for a certain period of time.
Public Boolean awaitTermination(long timeout, TimeUnit Unit) The waiting thread pool is TERMINATED. Timeout is the TERMINATED wait time and unit is the unit of time.
Public void execute(Runnable command) Submits tasks to the thread pool.
Public int getActiveCount() getActiveCount() getActiveCount() getActiveCount() getActiveCount() public int getActiveCount() getActiveCount() getActiveCount() getActiveCount() getActiveCount() public int getActiveCount() getActiveCount() getActiveCount() public int getActiveCount() getActiveCount() getActiveCount() Otherwise it’s free.
Public Long getCompletedTaskCount() Specifies the number of completed tasks in the thread pool.
Public Long getTaskCount() Total number of tasks in the thread pool (both completed and executing)
Public int getCorePoolSize() Specifies the number of core threads.
Public int getMaximumPoolSize() Specifies the maximum number of threads allowed in the thread pool.
Public int getPoolSize() Specifies the number of active threads in the thread pool
Public int getLargestPoolSize() Specifies the maximum number of threads that have been started in the thread pool.
Public Long getKeepAliveTime(TimeUnit Unit) Gets the allowed idle time of the thread pool.
Public BlockingQueue getQueue() getQueue() getQueue() public BlockingQueue getQueue() getQueue() getQueue() getQueue() getQueue()
Public ThreadFactory getThreadFactory() gets the factory class that creates the thread.
Public void shutdown() Closes the thread pool. This method interrupts only idle threads.
Public List shutdownNow() closes the thread pool immediately. Interrupts all threads and empties the task queue. Shutdown () and shutdownNow() will be analyzed later.
Public void setCorePoolSize(int corePoolSize) Sets the number of core threads.
Public void setMaximumPoolSize(int maximumPoolSize) Sets the maximum number of threads.
Public void setKeepAliveTime(long time, TimeUnit Unit) Sets the maximum idle time for a thread.
3. Several ways to enable thread pools
Basic opening mode
Construct the ThreadPoolExecutor object:
// Construct ThreadPoolExecutor directly
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3.4.60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
for (int i = 0; i < 10; i++) {
int ii = i;
threadPoolExecutor.execute(() -> {
System.out.println("runnable i=" + ii);
System.out.println("active count:" + threadPoolExecutor.getActiveCount() + " pool size:" + threadPoolExecutor.getPoolSize());
try {
Thread.sleep(1000);
} catch(InterruptedException e) { e.printStackTrace(); }}); }Copy the code
Although thread pools provide several overloaded constructors to use thread pools, the constructors have too many arguments to reuse. Thread pool provides the Executors class to wrap the construction thread pool object.
The following is a brief analysis of their principles and usage scenarios.
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor(a) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1.1.0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
Copy the code
The thread pool created in this way has only one core thread, the maximum number of threads is 1, and no timeout is set. When subsequent tasks arrive, they are put into a queue, which is theoretically infinite.
demo:
ExecutorService singleExecutorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
int ii = i;
singleExecutorService.execute(() -> {
System.out.println("runnable i=" + ii);
try {
Thread.sleep(1000);
} catch(InterruptedException e) { e.printStackTrace(); }}); }Copy the code
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
Copy the code
Specifies the number of core threads and the maximum number of threads, with no timeout. When subsequent tasks arrive, they are put into a queue, which is theoretically infinite. demo:
ExecutorService fixedExecutorService = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; i++) {
int ii = i;
fixedExecutorService.execute(() -> {
int activeCount = ((ThreadPoolExecutor)fixedExecutorService).getActiveCount();
System.out.println("runnable i=" + ii + " activeCount:" + activeCount);
try {
Thread.sleep(1000);
} catch(InterruptedException e) { e.printStackTrace(); }}); }Copy the code
newCachedThreadPool
public static ExecutorService newCachedThreadPool(a) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
Copy the code
The number of core threads is 0, the maximum number of threads is integer. MAX_VALUE, the timeout is 60 seconds, and the SynchronousQueue is used. SynchronousQueue is an uncached queue. Put and Take must be paired. Take the producer-consumer as an example. The producer produces something and blocks if there is no consumer to consume it, and vice versa if the consumer has nothing to take from it. When used with thread pools, the flow is as follows:
1. Submit the task for the first time and start a new thread to execute the task. 2. If the task is completed, the thread blocks and waits for a new task. 3. The new task is submitted, and the previously blocked thread is awakened to execute the task. 4. If the second thread has not completed the task and a new task comes, a new thread will be started to execute the task. 5. If the idle time exceeds 60 seconds, the thread will be reclaimed.
demo:
ExecutorService cacheExecutorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
int ii = i;
cacheExecutorService.execute(() -> {
int activeCount = ((ThreadPoolExecutor)cacheExecutorService).getActiveCount();
System.out.println("runnable i=" + ii + " activeCount:" + activeCount);
});
}
Copy the code
The log shows that six threads were created in a short time, so creating a thread pool in this way risks creating a large number of threads in a short time.
newScheduledThreadPool
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue(), threadFactory);
}
Copy the code
Specifies the number of core threads, integer. MAX_VALUE, a timeout of 10 milliseconds, and a DelayedWorkQueue (DelayedWorkQueue). ScheduledThreadPoolExecutor for more time to perform a task. demo:
ScheduledExecutorService scheduleExecutorService = Executors.newScheduledThreadPool(2);
for (int i = 0; i < 10; i++) {
int ii = i;
scheduleExecutorService.schedule(() -> {
int activeCount = ((ThreadPoolExecutor)scheduleExecutorService).getActiveCount();
System.out.println("runnable i=" + ii + " activeCount:" + activeCount);
}, 2, TimeUnit.SECONDS);
}
Copy the code
newWorkStealingPool
This method is introduced after JDK1.8 (inclusive), belongs to the preemptive task thread pool, and the previous four implementations are quite different.
demo:
ExecutorService stealService = Executors.newWorkStealingPool();
for (int i = 0; i < 10; i++) {
int ii = i;
stealService.execute(() -> {
System.out.println("runnable i=" + ii);
});
}
Copy the code
The following creation modes apply to the following scenarios:
Of course, if the existing encapsulation method does not meet the business requirements, then you need to construct your own ThreadPoolExecutor object. This method is cumbersome, but has the most freedom to meet the requirements of customization.
Another thing to note:
There are few ExecutorService methods, so you can convert the ExecutorService to ThreadPoolExecutor if you want to get more parameters from the thread pool. The ExecutorService returned by newSingleThreadExecutor and newWorkStealingPool cannot be forcibly converted to ThreadPoolExecutor.
How do I close/stop the thread pool
Two methods were mentioned earlier when listing common apis: shutdown() and shutdownNow().
shutdown()
public void shutdown(a) {
final ReentrantLock mainLock = this.mainLock;
/ / lock first
mainLock.lock();
try {
checkShutdownAccess();
// Change the state to SHUTDOWN
advanceRunState(SHUTDOWN);
// Interrupt idle threads
interruptIdleWorkers();
onShutdown(); // Subclasses can set hook methods
} finally {
mainLock.unlock();
}
// Try to enter the TIDYING state
tryTerminate();
}
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
/ / traverse the workers
for (Worker w : workers) {
Thread t = w.thread;
// The thread is not interrupted and can acquire the lock
if(! t.isInterrupted() && w.tryLock()) {// If the thread can acquire the lock, it is free
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally{ w.unlock(); }}if (onlyOne)
break; }}finally{ mainLock.unlock(); }}Copy the code
This method only interrupts the idle thread and does not affect the thread executing the task or the tasks in the queue.
shutdownNow()
public List<Runnable> shutdownNow(a) {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// Change the status to Stop
advanceRunState(STOP);
// Interrupt all threads
interruptWorkers();
// Clear the task queue
tasks = drainQueue();
} finally {
mainLock.unlock();
}
// Try to enter the TIDYING state
tryTerminate();
// Return the cleared task queue
return tasks;
}
private void interruptWorkers(a) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Interrupt all threads
for (Worker w : workers)
w.interruptIfStarted();
} finally{ mainLock.unlock(); }}Copy the code
As the name of the method indicates, to stop the thread pool immediately, all threads must be interrupted. Here you may be wondering: Can thread.interrupt () interrupt a Thread that is not blocking? The answer is: no. If you can’t interrupt, what’s the point? The answer is: Wake up the blocking thread and use the STOP and SHUTDOWN states to terminate the thread. Java gracefully Interrupts threads (Principles)
Thread pools do not have the ability to stop a thread individually, either at all or at all. Thread pooling downplays the concept of a single thread and focuses more on task scheduling.
If the demo code helps, give Github a thumbs up
At this point, the analysis of thread pool knowledge is complete.