00-总结
死锁的避免
- 持有并等待。尝试获取锁,超时则释放已有锁并重试。
- 保证所有线程获取锁的顺序完全一致
shutdown()和shutdownNow()
调用线程池的shutdown()或shutdownNow()方法,线程当前状态如何处理
| 线程当前状态 | shutdown() | shutdownNow() |
|---|---|---|
| 正在执行任务 | 继续执行,完成后退出 | 中断线程,但任务可能继续执行,取决于是否响应中断,不是"完成后退出",而是任务执行完后检测到 STOP 状态退出,如果任务不响应中断,会一直执行到完成(可能无限循环) |
| 等待获取任务 | 中断空闲线程, 线程在 workQueue.take() 处被中断,抛出InterruptedException异常,线程池自己捕捉到这个异常,继续自旋判断如果队列为空,rerurn null后,线程自然结束。 | 中断所有线程, 线程在 workQueue.take() 处被中断,抛出InterruptedException异常,线程池自己捕捉到这个异常,继续自旋判断发现是STOP,不管任务队列是否为空直接rerurn null后,线程自然结束。 |
| 刚获取任务 | 正常执行该任务 | 中断线程,任务可能执行也可能不执行,任务可能继续执行,取决于是否响应中断,不是"完成后退出",而是任务执行完后检测到 STOP 状态退出,如果任务不响应中断,会一直执行到完成(可能无限循环) |
| 新提交任务 | 拒绝(抛出异常) | 拒绝(抛出异常) |
| 线程池状态 | SHUTDOWN | STOP |
- shutdown()会等所有任务执行完成后。
- shutdownNow()正在处理的是否继续(看业务逻辑有没有中断检查),没有处理的在任务队列的shutdownNow()方法返回
线程状态
①,新建:new关键字创建了一个线程之后,该线程就处于新建状态;JVM为线程分配内存,初始化成员变量值
②,就绪:当线程对象调用了start()方法之后,该线程处于就绪状态;JVM为线程创建方法栈和程序计数器,等待线程调度器调度
③,运行:就绪状态的线程获得CPU资源,开始运行run()方法,该线程进入运行状态
④,阻塞:阻塞状态是指线程因为某种原因放弃了 cpu 使用权.阻塞状态分为三种情况:
等待阻塞:运行状态的线程调用了wait()方法后,该线程会释放它所持有的锁,然后被jvm放入到等待池中,只有等其他线程调用Object类的notify()方法或者norifyAll()方法时,才能进入重新进入到就绪状态。
同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,JVM就会把该线程设置为阻塞状态,一直到线程获取到同步锁,才能转入就绪状态。
其它阻塞:运行的线程在执行sleep()或者join()方法时,或者发出了I/O请求,JVM就会把该线程设置为阻塞状态,当sleep()状态超时、join()等待等待线程终止或者超时、或者I/O处理完毕时,线程重进转入到就绪状态。在这需要注意的是sleep()方法和wait()不同,sleep不会释放自身所持有的锁。
注意:程序调用了线程的suspend()方法将该线程挂起。但这个方法容易导致死锁,所以应该尽量避免使用该方法
⑤,死亡:线程会以如下3种方式结束,结束后就处于死亡状态:
- run()或call()方法执行完成,线程正常结束。
- 线程抛出一个未捕获的Exception或Error,线程异常结束。
线程池状态

1、RUNNING
- 状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
- 状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处 于RUNNING状态,并且线程池中的任务数为0
2、 SHUTDOWN
- 状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
- 状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。
3、STOP
- 状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
- 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。
4、TIDYING
- 状态说明:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
- 状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。 当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。
5、 TERMINATED
- 状态说明:线程池彻底终止,就变成TERMINATED状态。
- 状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。
进入TERMINATED的条件如下: 线程池不是RUNNING状态; 线程池状态不是TIDYING状态或TERMINATED状态; 如果线程池状态是SHUTDOWN并且workerQueue为空; workerCount为0;设置TIDYING状态成功
interrupt()与interrupted()与isInterrupted()
interrupted()属于类方法,而interrupt()和isInterrupted()属于对象方法。
interrupt():将中断状态修改为true,不会真正停止线程。仅此而已。
- 如果当前线程处于正常运行状态,线程将继续正常运行,不受影响。
- 如果当前线程处于阻塞状态(调用sleep, wait, join 等方法),别的线程调用当前线程的interrupt方法,那么当前线程中断状态将被清除重置为false,并抛出一个InterruptedException异常。
interrupted(): 获取线程的中断状态,但是会清除打断标记,假设将当中断标记设为true,调用一次interrupted()后将打断标记设为了false;
isInterrupted():获取线程的中断状态,不会更改打断标记,即连续两次调用该方法得到的结果都是true。
park()和wait()
ReentrantReadLock的锁保存在哪里
- 写锁的持有者:AQS的
exclusiveOwnerThread字段保存当前写锁的持有者。state高16位维护重入次数。 - 读锁的持有者:读锁的持有者信息保存在每个线程自己的
ThreadLocal中,每个线程会维护一个HoldCounter对象,count:该线程获取读锁的重入次数。tid:线程 ID。state低16位读锁的重入次数 = HoldCounter的count的总和
ThreadLocalMap存储结构:
key:ThreadLocal本身弱引用
value:map
key:readHolds锁的
value:HoldCounter
{
count :线程重入次数
tid:线程的ID
}