JAVA高并发编程--第三章读书笔记

 

中断响应

 

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框架

 

一些并发容器

©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页