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