The thread pool can solve two different problems: because it reduces the overhead of each task call, they can generally provide enhanced performance when executing a large number of asynchronous tasks, you can also bind and manage resources (including the threads used to execute the task set. EachThreadpoolexecutorSome basic statistics, such as the number of completed tasks, are also maintained.
This class provides many adjustable parameters and extended hooks for ease of use across a large number of contexts ). However, it is strongly recommended that programmers useExecutorsFactory methodExecutors.newCachedThreadPool()(The unbounded thread pool can be used for automatic thread recovery ),Executors.newFixedThreadPool(int)(Fixed size thread pool) andExecutors.newSingleThreadExecutor()(Single background thread), which are predefined for most use cases. Otherwise, use the following instructions when manually configuring and adjusting the class:
-
Core and maximum pool size
- ThreadpoolexecutorAccording to corepoolsize (see
getCorePoolSize()) And maximumpoolsize (see
getMaximumPoolSize()) Sets the boundary to automatically adjust the pool size. When the new task is in
execute(java.lang.Runnable)When submitting, if the number of running threads is less than corepoolsize, a new thread is created to process the request, even if other auxiliary threads are idle. If the number of running threads is greater than corepoolsize and less than maximumpoolsize, a new thread is created only when the queue is full. If the set corepoolsize and maximumpoolsize are the same, a fixed thread pool is created. If you set maximumpoolsize to a basic unbounded value (for example Integer. max_value), The pool is allowed to adapt to any number of concurrent tasks. In most cases, the core and maximum pool sizes are only set based on the constructor, but you can also use
setCorePoolSize(int)And
setMaximumPoolSize(int)Make dynamic changes.
-
Construct on demand
-
By default, you can use
prestartCoreThread()Or
prestartAllCoreThreads()Rewrite it dynamically. If you construct a pool with a non-empty queue, you may want to start the thread in advance.
-
Create a thread
-
Use
ThreadFactoryCreate a new thread. Otherwise
ThreadGroupAll in use
Executors.defaultThreadFactory()Create threads, and these threads have the same Norm_priorityPriority and non-daemon status. By providing different threadfactory, you can change the thread name, thread group, priority, and daemon status. If NewthreadReturn null ThreadfactoryIf a thread cannot be created, the program continues to run, but no task can be executed.
-
Maintain activity time
-
If there are more threads in the pool than corepoolsize, these extra threads will be terminated when the idle time exceeds KeepAliveTime (see
getKeepAliveTime(java.util.concurrent.TimeUnit)). This provides a way to reduce resource consumption when the pool is inactive. If the pool becomes more active later, a new thread can be created. You can also use
setKeepAliveTime(long, java.util.concurrent.TimeUnit)Modify this parameter dynamically. Use Long. max_value
TimeUnit.NANOSECONDSThe value of can effectively disable idle threads from the previous termination status before closing. By default, the persistence activity policy is applied only when there are more threads than corepoolsizethreads. However, as long as the KeepAliveTime value is not 0,
allowCoreThreadTimeOut(boolean)This Timeout Policy can also be applied to core threads.
-
Queuing
-
All
BlockingQueueCan be used to transfer and maintain submitted tasks. You can use this queue to interact with the pool size:
- If the number of running threads is less than corepoolsize, executor always prefers to add new threads without queuing.
- If the running thread is equal to or greater than corepoolsize, executor always prefers to add requests to the queue without adding new threads.
- If the request cannot be added to the queue, a new thread is created, unless the creation of this thread exceeds the maximumpoolsize. In this case, the task is denied.
There are three common queuing policies:
- Submit directly.The default Job Queue option is
SynchronousQueueIt directly submits tasks to the thread without holding them. If there is no thread that can be used to run the task immediately, trying to add the task to the queue will fail, so a new thread will be constructed. This policy prevents locks when processing requests that may have internal dependencies. Direct submission usually requires unbounded maximumpoolsizes to avoid rejecting new tasks. This policy allows unbounded threads to grow when the command arrives continuously beyond the average number that the queue can handle.
- Unbounded queues.Use unbounded queues (for example
LinkedBlockingQueue) Will cause the new task to wait in the queue when all corepoolsize threads are busy. In this way, the created thread will not exceed the corepoolsize. (Therefore, the value of maximumpoolsize is invalid .) When each task is completely independent from other tasks, that is, task execution does not affect each other, it is suitable to use unbounded queues. For example, in a Web Page Server. This kind of queuing can be used to handle transient bursts of requests. This policy allows unbounded threads to grow when the command arrives continuously beyond the average number that the queue can handle.
- Bounded queue.When a limited maximumpoolsizes is used, a bounded Queue (such
ArrayBlockingQueue) Helps prevent resource depletion, but may be difficult to adjust and control. The queue size and the maximum pool size may need to be compromised: using large queues and small pools can minimize CPU usage, operating system resources, and context switching overhead, but may cause manual throughput reduction. If the tasks are frequently congested (for example, if they are I/O boundaries), the system may schedule a longer time than you permit for more threads. Using a small queue usually requires a large pool size, and the CPU usage is high, but it may encounter unacceptable scheduling overhead, which will also reduce the throughput.
-
Rejected task
-
When the executor has been disabled and the Executor uses the limited boundary for maximum thread and work queue capacity, and is saturated
execute(java.lang.Runnable)The new task submitted in
Reject. In the above two cases, ExecuteMethod will call its
RejectedExecutionHandlerOf
RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor)Method. The following provides four predefined handler policies:
- In the default
ThreadPoolExecutor.AbortPolicyThe handler is rejected and will throw the runtimeRejectedExecutionException.
- In
ThreadPoolExecutor.CallerRunsPolicyThe thread callsExecuteItself. This policy provides a simple feedback control mechanism to speed down the submission of new tasks.
- In
ThreadPoolExecutor.DiscardPolicyCannot be executed.
- In
ThreadPoolExecutor.DiscardOldestPolicyIf the execution program is not closed, the task in the Job Queue header will be deleted, and then the execution program will be retried (if it fails again, the process will be repeated ).
Define and use other typesRejectedExecutionHandlerClass is also possible, but you need to be very careful when doing so, especially when the policy is only used for a specific capacity or queuing policy.
-
Hook method
-
Such provision ProtectedRewritable
beforeExecute(java.lang.Thread, java.lang.Runnable)And
afterExecute(java.lang.Runnable, java.lang.Throwable)The two methods are called before and after each task is executed. They can be used to manipulate the execution environment; for example, reinitializing threadlocal, collecting statistics, or adding log entries. You can also rewrite the method.
terminated()To execute all special operations that need to be completed after executor is completely terminated.
If an exception is thrown by the hook or callback method, the Internal auxiliary thread will fail and terminate suddenly.
-
Queue Maintenance
-
Method
getQueue()Allow access to the work queue for monitoring and debugging purposes. It is strongly opposed to using this method for any other purpose.
remove(java.lang.Runnable)And
purge()These two methods can be used to help with storage recycle when a large number of queued tasks are canceled.
-
Termination
-
Program
AndNo other threads in the pool that are no longer referenced will automatically Shutdown. If you want to cancel the referenced pool (even if you forget to call
shutdown()), You must arrange the end of unused threads: set the appropriate hold activity time, use the lower boundary of the 0 Core Thread and/or set
allowCoreThreadTimeOut(boolean).
Extension example. Most extensions of this class can override one or more protected hook methods. For example, the following is a subclass that adds a Simple pause/Resume function:
class PausableThreadPoolExecutor extends ThreadPoolExecutor { private boolean isPaused; private ReentrantLock pauseLock = new ReentrantLock(); private Condition unpaused = pauseLock.newCondition(); public PausableThreadPoolExecutor(...) { super(...); } protected void beforeExecute(Thread t, Runnable r) { super.beforeExecute(t, r); pauseLock.lock(); try { while (isPaused) unpaused.await(); } catch(InterruptedException ie) { t.interrupt(); } finally { pauseLock.unlock(); } } public void pause() { pauseLock.lock(); try { isPaused = true; } finally { pauseLock.unlock(); } } public void resume() { pauseLock.lock(); try { isPaused = false; unpaused.signalAll(); } finally { pauseLock.unlock(); } } }