线程池使用
线程池中提交一个任务的流程是怎么样的?
- 创建ThreadPool
ThreadPoolExecutor executor = new ThreadPoolExecutor
(10,200,1000, TimeUnit.SECONDS,null);
ThreadPoolExecutorc参数
- 核心线程数corePoolSize
- 最大线程数maximumPoolSize
- keepAliveTime
- 时间单位
- workQueue
- 使用execute()方法提交一个Runnable对象
executor.execute(new Runnable(){
@Override
public void run(){
System.out.println("Hello ThreadPool");
}
- 判断当前线程池中的线程数是否小于corePoolSize
if (workerCountOf(c) < corePoolSize)
- 如果小于,则创建新线程并执行Runnable
if (addWorker(command, true))
- 如果大于等于,尝试【入队】将Runnable加入到workQueue中
if (isRunning(c) && workQueue.offer(command))
- 如果workQueue没满,入队等待被执行
if (isRunning(c) && workQueue.offer(command))
- 如果workQueue满了判断线程数和最大线程数(线程数 < maximumPoolSize)
else if (!addWorker(command, false))
int wc = workerCountOf(c);
if (wc >= CAPACITY
- 如果大于最大线程数 【执行拒绝策略】拒绝任务
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
- 如果小于最大线程数,【创建并执行】创建新线程,将当前Runnable作为线程要执行的第一个任务
线程池的核心线程数、最大线程数该如何设置
线程池中 2个重要的参数:
- corePoolSize 核心线程数
- maimumPoolSize 最大线程数
这2个参数如何设置?
线程池负责执行的任务分三种情况:
- CPU密集型
比如找出1-1000000中的素数
线程数为:CPU核心数 + 1
为了应对线程执行过程发生缺页中断或其他异常导致线程阻塞的请求。
- IO密集型
文件IO、网络IO
大部分时间都阻塞在IO上,假如现在有10个CPU,如果我们只设置了10个线程来执行IO型任务,那很有可能这10个线程都阻塞在IO上,这样10个CPU都没活干了,所以对于IO型任务,通常会设置 2*CPU核心数。
线程数 = CPU核心数 * (1 + 线程等待时间/线程运行总时间)
线程等待时间:指的是没有使用CPU的时间,比如阻塞在IO
线程运行总时间:指的是线程执行完某个任务的总时间。
- 混合型
线程池状态
默认RUNNING
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
| 状态 | 说明 |
|---|---|
| RUNNING初始化 | 会接收新任务并且会处理队列中的任务 |
| SHUTDOWN | BU会接收新任务并且会处理队列中的任务。处理完后中断所有线程。 |
| STOP | 不会接收新任务并且不会处理队列中的任务。直接中断所有线程。 |
| TIDYING | 所有线程都停止之后,线程池的状态就会转为TIDYING一旦达到此状态,就会调用线程池的terminated() |
| TERMINATED | terminated()执行完之后就会变为TERMINATED |
RUNNING - SHUTDOWN
- 手动调用shutdown
- 线程池对象GC时候调用finalize()从而调用shutdown()
RUNNING - STOP
- 手动调用shutdownNow()触发
shutdownNow();为什么先改状态再关闭线程?以防有新的线程再进来。
SHUTDOWN - STOP
-
手动调用shutdown()紧接着调用shutdownNow()触发
SHUTDOWN - TIDYING
- 线程池所有线程都停止后自动触发
STOP - TIDYING
- 线程池所有线程都停止后自动触发
TIDYING - TERMINATED
- 线程池自动调用terminated()后触发
TIDYING 一旦变成了TIDYING,会执行terminated方法。 执行完后会变成TERMINATED状态表示闲成真正真正被关闭掉了。
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
优雅的停止一个线程
如果调用了stop()方法会直接停掉线程,stop会不会释放锁。
释放synchronized锁,不会自动释放ReentrantLock锁。