中断响应
P74的代码,一开始死锁形成的原因是t1占用lock1请求lock2,t2占用lock2请求lock1,而后来死锁得以释放是因为
49行T2中断后,不再等待lock1,同时释放lock2。T1可以继续运行,而t2放弃任务直接退出。
如果这里是synchronize关键字形成的死锁,则无法像这里使用lock形成的死锁那么好处理。
注:
lockinterruptibly()方法比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够通过interrupt方法完成响应中断,即中断线程的等待状态。也就是说,当两个线程同时通过lock.lockinterruputibly()去获取某个锁时,假如此时线程A获取到了锁,而线程B只有等待,那么对线程调用threadB.interrupt()方法能够中断线程B的等待过程。
注意:当一个线程获取了锁之后,是不会被interrupt()方法中断的
因此当通过lockinterruptibly()方法获取某个锁时,如果不能获取到,只有进行等待的情况下,是可以响应中断的
而用synchronized修饰的话,当一个线程处于等待某个锁的状态,是无法被中断的,只有一直等待下去。(源于CSDN)
锁申请等待限时

公平锁
两个线程同时申请某个锁时,系统会从这两个线程中随机选一个并向其提供锁。这是非公平锁。
公平锁实现成本较大,一般不使用,

第二行代码,往构造函数输入true,则可构建公平锁。
Condition条件

第24行主线程unlock了重入锁,这样condition.signal()后,t1才能重新获得锁。
剩下的工具类看书吧,范例和文字都很简洁。
信号量
读写锁
倒计数器
循环栅栏
LockSupport
他和suspend和resume的区别在于,suspend和resume,先suspend后resume是没问题的,但是先resume后suspend有可能导致系统判定线程是runnable的,但线程其实是挂起的。
而LockSupport为每一个线程准备了一个许可,许可可用,park的时候就立即返回且将许可变为不可用。许可不可用,就阻塞。Unpack可以将许可变为可用。
所以P95的代码,如果t1先在run方法中碰到park,这时许可不可用(我个人认为许可默认不可用),t1阻塞。如果不unpark,t1就一直阻塞并持有synchronize上的锁,那么t2是没有办法执行run方法的,而如果t1 unpark了,那么park方法就会因为许可可用而立即返回(立即执行),run方法就结束了,放出锁给t2,t2继续执行run方法。如果先Unpark,那么许可变为可用,执行run中的park时,park立即返回并将许可变为不可用,此时t1执行完run,t2获得锁继续执行run方法,故unpark在park之前或之后执行都一样。Park方法执行后会明确该线程状态是waiting的。
Guava和RateLimiter限流
线程池
JDK提供一套Executor框架作为线程池,


FixedThreadPool的示例:


17行代码创建内有5个线程的线程池,18行代码提交十个任务。
ScheduleThreadPool
示例:

scheduleAtFixedRate

示例中的代码传入的参数是0,2
指的是初始延时时间为零,此时任务开始,后一个任务将在0+2时执行,再后一个任务会在0+2*2时执行。

如果是scheduleWithFixedDelay,则任务开始于初始延时时间,前一个任务结束时到下一个任务开始时,其中的时间间隔是long delay。
FixedThreadPool,SingleThreadExecutor和CacheThreadPool都利用了ThreadPoolExecutor来实现。

ThreadPoolExecutor构造函数:


其中的workQueue:


拒绝策略

这四个都是RejectedExecutorHandler接口的实现类。
Fork/Join框架