《Java并发编程》读书笔记
2022-04-24 10:04:45 1 举报
AI智能生成
并发编程的知识属于Java高阶知识,对其的掌握有利于在日常开发中写出更好的并发代码
作者其他创作
大纲/内容
管程模型
管理共享变量以及对共享变量的操作过程,让他们支持并发
管程模型
MESA 模型
Hasen 模型
Hoare 模型
两大核心问题
互斥
共享变量及其对共享变量的操作统一封装起来
只允许一个线程进入管程
同步
条件变量
条件变量等待队列
synchronized
基于管程MESA模型实现
只支持一个条件变量
语言内置
等待-通知机制
wait
notify
notifyAll
解决了可见性、有序性、原子性问题
可重入锁
锁优化
无锁
偏向锁
轻量级锁
重量级锁
锁升级、锁降级
宏观问题
安全性
互斥锁
活跃性
死锁
发生条件
互斥
占有且等待
不可抢占
循环等待
解决之道
一次性申请所有资源
主动释放资源
资源排序按顺序获取
活锁
增加随机等待时间
饥饿
保证资源充足
公平
公平锁
避免持有锁的线程执行时间过长
性能
方向
尽量无锁
减少锁持有的时间
衡量标准
吞吐量
指的是单位时间内能处理的请求数量。吞吐量越高,说明性能越好
并发数
指的是能同时处理的请求数量,一般来说随着并发量的增加、延迟也会增加。所以延迟这个指标,一般都会是基于并发量来说的。例如并发量是 1000 的时候,延迟是 50 毫秒。
延迟
指的是从发出请求到收到响应的时间。延迟越小,说明性能越好
信号量模型
一个计数器,一个等待队列,三个方法
允许多个线程访问临界区
对管程模型的一种补充
ReadWriteLock
读写锁,针对读多写少的场景
支持读锁和写锁
读读不互斥,其他互斥
支持公平和非公平
读锁不支持条件变量,写锁支持
支持锁降级,不支持锁升级
可重入锁
CountDownLatch&CyclicBarrier
基于AQS实现
CyclicBarrier
一组线程之间互相等待
计算器可以循环
有回调函数
任务线程阻塞
借助ReentrantLock和Condition实现
构建对象传入值赋值给CyclicBarrier里的count,await就是利用ReentrantLock对count-1,count不为0就加入条件变量队列,count=0时队列移交给AQS队列并全部释放,重置count的值
CountDownLatch
一个线程等待多个线程
计算器不能循环
主线程阻塞
构建对象传入值给AQS的state赋值,countDown就是利用CAS将state-1,await就是等state=0
原子类
CAS硬件保证原子性
ABA问题
增加递增的版本号
原子化基本数据类型
AtomicBoolean
AtomicInteger
AtomicLong
原子化引用类型
AtomicReference
AtomicStampedReference
AtomicMarkableReference
原子化数组
AtomicIntegerArray
AtomicLongArray
AtomicReferenceArray
原子化的对象属性更新器
AtomicIntegerFieldUpdater
AtomicLongFieldUpdater
AtomicReferenceFieldUpdater
属性必须是volatile
反射实现
原子化累加器
DoubleAccumulator
DoubleAdder
LongAccumulator
LongAdder
不支持compareAndSet,适合仅仅使用累加的场景
线程池
ThreadPoolExecutor
corePoolSize
保持的最小线程数
1.6后提供allowCoreThreadTimeOut,允许空闲时回收corePollSize
maximumPoolSize
最大线程数
keepAliveTime & unit
空闲时间后,线程数不低于corePoolSize时进行回收
workQueue
任务队列
强烈建议使用有界队列,防止OOM
threadFactory
创建线程的工厂类
handler
拒绝策略
CallerRunsPolicy
提交任务的线程自己去执行该任务
AbortPolicy
默认,抛RejectedExecutionException
慎用
DiscardPolicy
直接丢弃任务,没有任何异常抛出
DiscardOldestPolicy
丢弃最老的任务,其实就是把最早进入工作队列的任务丢弃,然后把新任务加入到工作队列
建议自定义拒绝策略
execute执行不会抛异常,推荐任务内部自行捕获异常处理
Executors
线程池的静态工厂类
默认使用无界队列
初始化线程数
CPU密集型:CPU核数+1
IO密集型:CPU核数*(1+IO/CPU)
ExecutorService&Future
submit(Runnable task)
submit(Callable<T> task)
submit(Runnable task, T result)
Future.get()属于阻塞式操作
FutureTask
既可以返回结果也可以作为任务
CompletableFuture
ForkJoinPool线程池,线程数=CPU核数
利用重载方法自定义线程池
CompletionStage
串行
thenApply
thenAccept
thenRun
thenCompose
Async结尾代表异步执行
AND
thenCombine
thenAcceptBoth
runAfterBoth
Async结尾代表异步执行
OR
applyToEither
acceptEither
runAfterEither
Async结尾代表异步执行
异常处理
exceptionally
whenComplete
handle
Async结尾代表异步执行
CompletionService
基于线程池+阻塞队列
批量执行异步任务
Fork/Join
ForkJoinPool
ForkJoinTask
RecursiveAction
RecursiveTask
递归
多个双端任务队列,正常消费和任务窃取各在一端进行
本质问题
可见性
高速缓存(L1,L2)
有序性
编译优化
编译器
指令集重排序
内存系统重排序
原子性
线程切换
Java内存模型
屏蔽硬件和操作系统访问内存的各种差异,保证了Java程序在各种平台下对内存的访问都能得到一致效果
抽象结构
主内存、工作内存
8种原子操作read/load/use/assign/store/write/lock/unlock
happen-before
顺序性原则
volatile变量规则
写对后续的读可见
传递性规则
A Happens-Before B,且 B Happens-Before C,那么 A Happens-Before C
管程中锁规则
unlock发生在下一个lock之前
线程start规则
主线程 A 启动子线程 B 后,子线程 B 能够看到主线程在启动子线程 B 前的操作
线程join规则
主线程 A 等待子线程 B 完成(主线程 A 通过调用子线程 B 的 join() 方法实现),当子线程 B 完成后(主线程 A 中 join() 方法返回),主线程能够看到子线程的操作
线程中断规则
对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测到是否有中断发生
对象终结规则
一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始
volatile
内存屏障
解决了可见性和有序性问题
final
避免逸出
线程
本质就是OS的线程
生命周期
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED
Lock&Condition
基于管程MESA模型实现
弥补synchronized的不足
响应中断
支持超时
非阻塞式获取锁
利用了 volatile 相关的 Happens-Before 规则保证可见性,有序性和原子性
支持多个条件变量
等待-通知机制
await
signal
signalAll
dubbo的异步转同步实现原理
支持公平和非公平
可重入锁
Semaphore
基于信号量模型实现
限流器
线程池、连接池中广泛使用
StampedLock
ReadWriteLock的升级版本
支持写锁、悲观读锁和乐观读
不可重入锁
乐观读期间验证是否有写操作,有的话升级成悲观读锁
乐观读期间允许写操作
都不支持条件变量
不支持中断,需要支持获取readLockInterruptibly和writeLockInterruptibly
支持锁升级和降级tryConvertTo***
并发容器
同步容器
Collections.synchronized****
List
CopyOnWriteArrayList
适合写非常少,且能接受短暂不一致的场景
迭代器只读
Map
ConcurrentHashMap
key无序
ConcurrentSkipListMap
key有序
跳表
Set
CopyOnWriteArraySet
ConcurrentSkipListSet
Queue
单端阻塞队列
ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue
LinkedTransferQueue
PriorityBlockingQueue
DelayQueue
双端阻塞队列
LinkedBlockingDeque
单端非阻塞队列
ConcurrentLinkedQueue
双端非阻塞队列
ConcurrentLinkedDeque
边界问题
并发设计模式
同步&互斥
Immutability模式
类和属性都是final,所有方法只读
如:String,Long等
享元模式
对象池
注意点
对象的所有属性都是 final 的,并不能保证不可变性
不可变对象也需要正确发布
无状态
Copy-on-Write模式
写时复制
耗内存
线程本地存储模式
局部变量
ThreadLocal
充当Thread的静态代理
内存泄漏
finally中remove
子线程不能继承父线程的线程变量
InheritableThreadLocal
子线程继承父线程的线程变量
Guarded Suspension模式
保护性暂停
等待一个条件满足
等待-唤醒机制
如:dubbo异步转同步,等待服务端的返回
原理:管程的await和signalAll
扩展:请求线程与返回消息处理绑定
Balking模式
等待但不暂停,不满足就退出,等下次
多线程版本的if
如:自动保存路由表(RPC框架),编辑器
需要引入互斥锁实现
volatile优化锁粒度
分工
Thread-Per-Message模式
委托代理模式
如:网络服务端为每个请求创建一个线程处理
轻量级线程——协程
OpenJDK——Fiber
适合轻量级线程
JAVA领域容易OOM
Worker Thread模式
线程池+阻塞队列
有界队列+合理拒绝策略+自定义线程名称
死锁
提交线程池的任务有依赖
调大线程数量
任务对应线程池独立
提交到相同线程池中的任务一定是相互独立的,否则就一定要慎重
两阶段终止模式
优雅地终止线程
两阶段
发送终止指令
interrupt()
响应终止指令
isTerminated
优雅地终止线程池
shutdown
会拒绝接收新的任务,但是会等待线程池中正在执行的任务和已经进入阻塞队列的任务都执行完之后才最终关闭线程池
shutdownNow
会拒绝接收新的任务,同时还会中断线程池中正在执行的任务,已经进入阻塞队列的任务也被剥夺了执行的机会,不过这些被剥夺执行机会的任务会作为 shutdownNow() 方法的返回值返回
需要正确地处理线程中断
生产者-消费者模式
核心:任务队列
优点
解耦
支持异步,并且能够平衡生产者和消费者的速度差异
应用
线程池
批量提交
先放队列,满了一起提交
分阶段提交
如:日志异步刷盘
Actor模型
特点
Actor 是一种基础的计算单元
在 Actor 模型中,所有的计算都是在 Actor 中执行的
Actor 之间是完全隔离的,不会共享任何变量
单线程处理消息
异步
不需要在同一个进程中,也不需要在同一台机器上
基于消息通信
类库
Akka
Vert.x
规范化定义
能力
处理能力,处理接收到的消息
存储能力,Actor 可以存储自己的内部状态,并且内部状态在不同 Actor 之间是绝对隔离的
通信能力,Actor 可以和其他 Actor 之间通信
处理消息
创建更多的 Actor
发消息给其他 Actor
确定如何处理下一条消息
本质上不过是改变内部状态
收藏
收藏
0 条评论
下一页