Runnable and Callable
- Runnable 没有返回值
- Callable 有返回值
1 | class MyThread implements Runnable { |
线程池
线程池优势
- 降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。 当任务到达时,任务可以不需要的等到线程创建就能立即执行。
- 提高线程的可管理性。 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,
- 使用线程池可以进行统一的分配,调优和监控。
线程池的创建
Executors
- Executors.newFixThreadPool(n) : 创建指定个数的线程池
- Executors.newSingleThreadExecutor: 创建只有一个线程的线程池
- Executors.newCacheThreadPool: 适用于不定个数的短期任务的执行,通过内部的调度,根据实际的情况动态地调整线程池中的线程数量
部分底层创建源码(ThreadPoolExecutor)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
注意
- submit有返回值,而execute没有
ExecutorService的execute和submit方法
ThreadPoolExecutor(阿里推荐)
1 | /** |
七大参数
- corePoolSize: 线程池中常驻核心线程数
- maximumPoolSize: 线程池能够容纳同时执行的最大线程数,此值必须大于等于1
- keepAliveTime:多余的空闲线程的存活时间,当前的线程池的数量超过corePoolSize 时,当空闲时间达到keepAliveTime 值时,多余的空闲线程会被销毁直到剩下corePoolSize 个线程为止。
- unit:keepAliveTime 的单位
- workQueue:阻塞任务队列,被提交但尚未被执行的任务(当任务队列满时,线程池会扩充线程数,扩充后的线程数会小于maximumPoolSize)
- threadFactory:表示生成线程的线程工厂,一般使用默认便可
- RejectedExecutionHandler:拒绝策略,当阻塞队列已满而且活跃线程数已经达到了maximumPoolSize 后,会启动拒绝策略,拒绝请求的加入
- AbortPolicy(默认):该策略直接抛出RejectedExecutionException 异常阻止系统的正常运行
- CallerRunsPolicy:该策略不会抛弃任务也不会抛出异常,而是将任务退回到调用者
- DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入到队列中再次提交该任务
- DiscardPolicy:直接丢弃任务,不给予任何的处理,也不抛出异常。
1 | ExecutorService executorService = new ThreadPoolExecutor( |
线程池的工作流程
- 在创建了线程池后,等待提交过来的任务请求
- 当调用了execute() 方法添加了一个请求任务后,线程池会做如下判断:
- 如果正在运行的线程池数量小于corePoolSize,那么马上创建线程运行这个任务
- 如果正在运行的线程数量大于或等于corePoolSize时,那么会将请求任务放到阻塞队列中
- 如果阻塞队列已经满了,而且正在运行的线程数量小于maximumPoolSize,那么会创建非核心线程来立即运行这个任务
- 如果阻塞队列已经满了,而且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动拒绝策略
- 当一个线程执行完任务时,会从队列中取出下一个任务出来继续执行
- 当一个线程的空闲时间大于kiveAliveTime 时,会关闭一些线程,直到线程数达到corePoolSize 为止
几张参考图
线程池线程数的合理配置
CPU 密集型
CPU 密集型需要大量的计算,没有阻塞,CPU 一直在全速运行
一个公式:CPU 核数 + 1个线程的线程池
IO 密集型
IO 密集型,需要大量的IO 操作,即大量的阻塞
在单线程上运行IO 密集型的任务会导致浪费大量CPU 运算能力浪费在等待上,所以在IO 密集型任务中运行多线程k可以大大加速程序的运行
参考公式:CPU 核数/(1-阻塞系数),阻塞系数在0.8-0.9 之间
死锁的编码以及定位分析
1 | class Resource implements Runnable{ |
定位分析(jps + jstack)
1 | Java stack information for the threads listed above: |