线程池的基本使用
为什么要用线程池
- 线程生命名周期的开销非常高
- 资源消耗,活跃的线程数越多,消耗的资源越多,尤其是内存。
- 线程复用
JDK为我们提供了哪些支持
Executor框架
- Executor 定义execute接口。
- ExecutorService 对外接口,主要使用这个来引用一个线程池。
ThreadPoolExecutor
线程池核心实现类Executors 工厂类,通过创建不同的
ThreadPoolExecutor
实现不同功能的线程池,类似于这样创建ExecutorService pool = Executors.c=newXXXPool(...)
- 常用池种类
- 线程数量固定
- 线程数量动态
- 单一线程
- 按时间计划,依次执行的池。
- 常用池种类
线程池的几种状态
- RUNNING 正常运行状态
- SHUTDOWN 不接受新任务,处理任务队列中的任务
- STOP 不接受任务,尝试中断任务队列中的任务
- TIDYING 线程池为空
- TERMINATED 线程池关闭
- 线程池原理
- 生产者消费者 模式
HashSet<Worker> workers
保存已经创建了的线程。BlockingQueue workQueue
保存需要执行的任务,任务队列的规模是无限大的。- ctl
AtomicInteger
以位运算的方式打包封装了当前线程池ThreadPoolExecutor对象的状态和活动线程数两个数据
几种不同的线程池比较
线程池 | 保存任务的QUEUE | 最大线程数 |
---|---|---|
newFixedThreadPool | LinkedBlockingQueue | 用户指定 |
newCachedThreadPool | SynchronousQueue | Integer.MAX_VALUE |
newScheduledThreadPool | DelayedWorkQueue | Integer.MAX_VALUE |
newSingleThreadExecutor | LinkedBlockingQueue | 1个 |
- ArrayBlockingQueue
- 使用数组保存数据
- ReentrantLock 单锁双条件、notEmpty、notFull
- 用户指定容量 capacity , 用户指定 boolean fair 是否是公平锁
- LinkedBlockingQueue
- 使用Node保存数据
- 默认大小Int.max
- 使用了两个锁 putLock-notFull、takeLock-notEmpty,双锁双条件
- DelayQueue
- 容量无界
- 单锁单条件 takeLock-notEmpty
- 实际上是对优先级队列PriorityQueue类的一个封装
- 对放入的元素有要求
<E extends Delayed>
- SynchronousQueue
- 没用容量概念
- 内部类TransferQueue和TransferStack
- 一个元素只有take和put都被阻塞的时候才会互相唤醒,即队列中只会有一个元素。
- 阻塞机制 使用的是 LockSupport的park()和unPark()方法。