什么是线程池
线程池 = worker线程 + 任务队列
worker线程
继承自AQS,并且实现了Runnable接口。worker线程不断从任务队列里获取任务并执行,当任务队列里面没有任务可执行了或者任务执行途中抛出异常时,线程退出,销毁线程。
AQS
AQS为那些基于FIFO等待队列的同步器和阻塞锁提供了一个框架
AQS为那些用单个原子int值表示状态的同步器,提供了状态的原子操作(getState、setState、compareAndSetState)
同步器要继承自AQS,并作为非public的内部helper类,具体的public同步方法由外围包装类提供
AQS支持互斥、共享两种获取锁的模式,子类同步器一般实现其中一种模式,也可以实现两种模式供用户选择,如ReadWriteLock
AQS定义了嵌套类ConditionObject 实现了Condition接口,支持互斥模式的子类同步器可以使用此对象来实现Object内置的wait-notify语义
子类同步器需要实现以下方法,且要保证实现方法的线程安全,修改或者获取状态使用AQS提供的方法getState、setState、compareAndSetState
tryAcquire
tryRelease
tryAcquireShared
tryReleaseShared
isHeldExclusively
AQS内部的FIFO队列的使用方式如下:
1 | Acquire: |
如果AQS不能满足你的同步器需求,你可以基于更底层的java.util.concurrent.atomic类、java.util.Queue类和LockSupport类来实现自定义的同步器
AQS使用举例:
Mutex类:互斥锁,状态0代表unlock,状态1代表locked
1 | class Mutex implements Lock, java.io.Serializable { |
CLH队列:基于链表的确保无饥饿的、提供先来先服务的,公平的自旋锁,申请线程只在本地变量上自旋,它【不断轮询前驱的状态,如果发现前驱释放了锁就结束自旋,获得锁】
当一个线程获取锁时:
1. 创建Node节点:封装线程+状态,状态置为:获取锁, 加入FIFO队列队尾
2. 获取队列里的前驱节点,自旋检查前驱节点的状态,直到前驱节点状态为:释放锁 时,自己获取到锁,并回收前驱节点
当一个线程释放锁时:
1. 释放锁的节点必然是队列头节点,设置自己的状态为:释放锁
等待队列:CLH队列的变种 用于阻塞同步
当一个线程获取锁时: 没获取到,会加入队尾,并且使用LockSupport.park 阻塞当前线程,进入睡眠状态
当上一个节点释放锁时,使用LockSupport.unpark 去通知后继节点,唤醒相应的线程
任务队列
任务队列实现BlockingQueue接口,细分为:
无界队列:DelayedWorkQueue、LinkedBlockingQueue
有界队列:ArrayBlockingQueue
直接递交给worker线程型队列(队列不存放任务):SynchronousQueue