I. Introduction
What is the thread pool
Thread pool is a multithreaded processing forms processing tasks will be added to the queue, and then start these tasks automatically when a thread is created.
Why use the thread pool
If the number of concurrent requests a lot, but each thread execution time is very short, frequent creating and destroying threads will appear. In this way, it will greatly reduce the efficiency of the system, may frequently creating and destroying threads of time, resource overhead required to be greater than the actual work.
It is because of this problem, it is necessary to introduce the thread pool. Use the benefits of thread pools are the following:
- Reduce resource consumption - reduce thread creation and destruction caused by the consumption by reusing threads that have been created.
- Improve response time - when the task arrives, the task may not need to wait until the thread creation can be implemented immediately.
- Improve the manageability of the thread - the thread is a scarce resource, if the unlimited creation, not only consumes system resources, but also reduce the stability of the system, using a thread pool can be unified distribution, tuning and monitoring. But to be a reasonable use of the thread pool must be well aware of its principle.
Two, Executor framework
Executor framework is a framework based on a set of asynchronous task execution strategy calls, scheduling, execution and control, the purpose is to provide a "job submission" mechanism and "How to run the task" of separated.
Core API Overview
Executor framework core API as follows:
Executor
- Simple interface to run the task.ExecutorService
- extends theExecutor
interface. Scalability:- Supports thread returns value;
- Support Lifecycle management thread.
ScheduledExecutorService
- extends theExecutorService
interface. Scalability: Support for recurring tasks.AbstractExecutorService
-ExecutorService
The default implementation of the interface.ThreadPoolExecutor
- Executor framework of the core class that inherits theAbstractExecutorService
class.ScheduledThreadPoolExecutor
-ScheduledExecutorService
implementation of the interface, a timed schedule thread pool tasks.Executors
- by callingExecutors
to create a thread pool static factory method and returns anExecutorService
object.
Executor
Executor
Interface only defines a execute
method for receiving an Runnable
object.
public interface Executor {
void execute(Runnable command);
}
ExecutorService
ExecutorService
Interface extends Executor
the interface, it provides invokeAll
, invokeAny
, shutdown
, submit
and other methods.
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
The method defined in support of its easy to see: Compared to Executor
the interface, ExecutorService
the main extension interface is:
- Support thread returns a value of
sumbit
-invokeAll
, ,invokeAny
methods are support incomingCallable
object. - Support Lifecycle management thread
shutdown
-shutdownNow
, ,isShutdown
and other methods.
ScheduledExecutorService
ScheduledExecutorService
Interface extends the ExecutorService
interface.
In addition to its ability to support all of the previous two interfaces, it also supports a timing schedule threads.
public interface ScheduledExecutorService extends ExecutorService {
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
}
Its expansion interface provides the following capabilities:
schedule
A method can be performed after a specified delayRunnable
orCallable
task.scheduleAtFixedRate
Methods andscheduleWithFixedDelay
ways specified time interval to perform regular tasks.
三、ThreadPoolExecutor
java.uitl.concurrent.ThreadPoolExecutor
Class is the Executor
framework in the core classes. Therefore, this paper focuses on what this class.
Important fields
ThreadPoolExecutor
It has the following important fields:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 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;
Parameter Description:
ctl
- the number of active threads used to control the operational status and thread pool thread pool . It contains information on two parts:- Operational state of the thread pool (
runState
) - The number of threads in the thread pool valid (
workerCount
) - It can be seen
ctl
using theInteger
type to save, save three highrunState
, 29 low saveworkerCount
.COUNT_BITS
Is 29,CAPACITY
is minus 1 left 29 1 (29 1), this constant represents theworkerCount
upper limit is about 500 million.
- Operational state of the thread pool (
- Operating status - thread pool a total of five operating states:
RUNNING
- operational status . To accept new tasks, and can also handle the task of blocking queue.SHUTDOWN
- off . We do not accept new tasks, but can handle the task of blocking queue.- In the thread pool in the
RUNNING
state, callingshutdown
the method causes the thread pool to enter the state. finalize
The method is also called during the executionshutdown
method to enter the state.
- In the thread pool in the
STOP
- Stop state . Do not accept the new task, it does not handle the task queue. Interrupted thread is processing tasks. In the thread pool is inRUNNING
orSHUTDOWN
state, call theshutdownNow
method will make the thread pool to enter the state.TIDYING
- finishing state . If all the tasks have been terminated,workerCount
after (effective number of threads) is 0, the thread pool to enter the state calls theterminated
method to enter theTERMINATED
state.TERMINATED
- terminated state . In theterminated
method performed after entering the state. The defaultterminated
method does nothing. EnterTERMINATED
the following conditions:- The thread pool is not a
RUNNING
state; - Thread pool state is not
TIDYING
a state orTERMINATED
state; - If the state is the thread pool
SHUTDOWN
andworkerQueue
empty; workerCount
0;- Set up
TIDYING
a successful state.
- The thread pool is not a
Construction method
ThreadPoolExecutor
There are four construction method, the first three are all based on the fourth implementation. The fourth method is the following structure:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
Parameter Description:
corePoolSize
- the number of kernel threads . When a new task byexecute
when submitting method, the thread pool performs the following judgment:- If fewer than the number of threads running
corePoolSize
, create a new thread to handle the task, even if other threads in the pool are idle. - If the number of threads in the pool or greater
corePoolSize
and lessmaximumPoolSize
, only ifworkQueue
when full to create a new thread to handle the task; - If the set
corePoolSize
andmaximumPoolSize
the same size is created in the thread pool is fixed. Then if there is a new job submission, ifworkQueue
not full, the request will be putworkQueue
in, there is a free thread to wait fromworkQueue
taking tasks and processes; - If the number of threads running greater than or equal
maximumPoolSize
, then ifworkQueue
full, then usehandler
the specified policy to deal with the task; - Therefore, the task is submitted, it is judged in the order of
corePoolSize
=>workQueue
=>maximumPoolSize
.
- If fewer than the number of threads running
maximumPoolSize
- maximum number of threads .- If the queue is full, and the number of threads that have been created less than the maximum number of threads, the thread pool will re-create a new thread to perform the task.
- 值得注意的是:如果使用了***的任务队列这个参数就没什么效果。
keepAliveTime
:线程保持活动的时间。- 当线程池中的线程数量大于
corePoolSize
的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime
。 - 所以,如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
- 当线程池中的线程数量大于
unit
-keepAliveTime
的时间单位。有 7 种取值。可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。workQueue
- 等待执行的任务队列。用于保存等待执行的任务的阻塞队列。 可以选择以下几个阻塞队列。ArrayBlockingQueue
- 有界阻塞队列。- 此队列是基于数组的先进先出队列(FIFO)。
- 此队列创建时必须指定大小。
LinkedBlockingQueue
- ***阻塞队列。- 此队列是基于链表的先进先出队列(FIFO)。
- 如果创建时没有指定此队列大小,则默认为
Integer.MAX_VALUE
。 - 吞吐量通常要高于
ArrayBlockingQueue
。 - 使用
LinkedBlockingQueue
意味着:maximumPoolSize
将不起作用,线程池能创建的最大线程数为corePoolSize
,因为任务等待队列是***队列。 Executors.newFixedThreadPool
使用了这个队列。
SynchronousQueue
- 不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。- 每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。
- 吞吐量通常要高于
LinkedBlockingQueue
。 Executors.newCachedThreadPool
使用了这个队列。
PriorityBlockingQueue
- 具有优先级的***阻塞队列。
threadFactory
- 线程工厂。可以通过线程工厂给每个创建出来的线程设置更有意义的名字。handler
- 饱和策略。它是RejectedExecutionHandler
类型的变量。当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。线程池支持以下策略:AbortPolicy
- 丢弃任务并抛出异常。这也是默认策略。DiscardPolicy
- 丢弃任务,但不抛出异常。DiscardOldestPolicy
- 丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)。CallerRunsPolicy
- 只用调用者所在的线程来运行任务。- 如果以上策略都不能满足需要,也可以通过实现
RejectedExecutionHandler
接口来定制处理策略。如记录日志或持久化不能处理的任务。
execute 方法
默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务之后才会创建线程。
提交任务可以使用 execute
方法,它是 ThreadPoolExecutor
的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。
execute
方法工作流程如下:
- 如果
workerCount < corePoolSize
,则创建并启动一个线程来执行新提交的任务; - 如果
workerCount >= corePoolSize
,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中; - 如果
workerCount >= corePoolSize && workerCount < maximumPoolSize
,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务; - 如果
workerCount >= maximumPoolSize
,并且线程池内的阻塞队列已满,则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。
其他重要方法
在 ThreadPoolExecutor
类中还有一些重要的方法:
submit
- 类似于execute
,但是针对的是有返回值的线程。submit
方法是在ExecutorService
中声明的方法,在AbstractExecutorService
就已经有了具体的实现。ThreadPoolExecutor
直接复用AbstractExecutorService
的submit
方法。shutdown
- 不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务。- 将线程池切换到
SHUTDOWN
状态; - 并调用
interruptIdleWorkers
方法请求中断所有空闲的 worker; - 最后调用
tryTerminate
尝试结束线程池。
- 将线程池切换到
shutdownNow
- 立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。与shutdown
方法类似,不同的地方在于:- 设置状态为
STOP
; - 中断所有工作线程,无论是否是空闲的;
- 取出阻塞队列中没有被执行的任务并返回。
- 设置状态为
isShutdown
- 调用了shutdown
或shutdownNow
方法后,isShutdown
方法就会返回 true。isTerminaed
- 当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed
方法会返回 true。setCorePoolSize
- 设置核心线程数大小。setMaximumPoolSize
- 设置最大线程数大小。getTaskCount
- 线程池已经执行的和未执行的任务总数;getCompletedTaskCount
- 线程池已完成的任务数量,该值小于等于taskCount
;getLargestPoolSize
- 线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过,也就是达到了maximumPoolSize
;getPoolSize
- 线程池当前的线程数量;getActiveCount
- 当前线程池中正在执行任务的线程数量。
使用示例
public class ThreadPoolExecutorDemo {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 500, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 100; i++) {
threadPoolExecutor.execute(new MyThread());
String info = String.format("线程池中线程数目:%s,队列中等待执行的任务数目:%s,已执行玩别的任务数目:%s",
threadPoolExecutor.getPoolSize(),
threadPoolExecutor.getQueue().size(),
threadPoolExecutor.getCompletedTaskCount());
System.out.println(info);
}
threadPoolExecutor.shutdown();
}
static class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行");
}
}
}
四、Executors
JDK 的 Executors
类中提供了几种具有代表性的线程池,这些线程池 都是基于 ThreadPoolExecutor
的定制化实现。
在实际使用线程池的场景中,我们往往不是直接使用 ThreadPoolExecutor
,而是使用 JDK 中提供的具有代表性的线程池实例。
newSingleThreadExecutor
创建一个单线程的线程池。
只会创建唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。 如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它 。
单工作线程最大的特点是:可保证顺序地执行各个任务。
示例:
public class SingleThreadExecutorDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 100; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行");
}
});
}
executorService.shutdown();
}
}
newFixedThreadPool
创建一个固定大小的线程池。
每次提交一个任务就会新创建一个工作线程,如果工作线程数量达到线程池最大线程数,则将提交的任务存入到阻塞队列中。
FixedThreadPool
是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。
示例:
public class FixedThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 100; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行");
}
});
}
executorService.shutdown();
}
}
newCachedThreadPool
创建一个可缓存的线程池。
- If the number of threads in the thread pool is longer than the processing tasks required, part of the idle thread will recover;
- If a long time did not submit the task to the thread pool, that is, if the worker thread has been idle for a specified time (default is 1 minute), then the worker thread will automatically terminate. Upon termination, but if you submitted a new task, the thread pool to re-create a worker thread.
- This thread pool do not restrict the size of the thread pool, thread pool thread maximum size depends entirely on the size of the operating system (or JVM) that can be created. Therefore, the use
CachedThreadPool
, be sure to pay attention to the number of control tasks, otherwise, due to the large number of threads to run simultaneously, great cause system down.
Example:
public class CachedThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行");
}
});
}
executorService.shutdown();
}
}
newScheduleThreadPool
Create an unlimited size of the thread pool. This thread pool to support the timing and the need to perform tasks periodically.
public class ScheduledThreadPoolDemo {
public static void main(String[] args) {
schedule();
scheduleAtFixedRate();
}
private static void schedule() {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 100; i++) {
executorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行");
}
}, 1, TimeUnit.SECONDS);
}
executorService.shutdown();
}
private static void scheduleAtFixedRate() {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 100; i++) {
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行");
}
}, 1, 1, TimeUnit.SECONDS);
}
executorService.shutdown();
}
}