同步器
同步器
同步器整体架构
六个同步器
- ReentrantLock
- Semaphore
- CyclicBarrier
- CountDownLatch
- Phaser
- Exchager
ReentrantLock和Synchronized异同
相同点:
- 临界区保护(提供锁/解锁的能力)
- 可重入
- 都提供线程间的协作
- Synchronized基于Monitor,monitor提供Object.wait(),Object.notify()
- ReentrantLock基于AQS,提供Condition Object,提供await(), signal()等方法
- 提供锁升级的逻辑
- Synchronized:偏向锁->轻量级锁->重量级锁
- ReentrantLock:CAS竞争->休眠+排队竞争
- 都提供等待队列
- Synchronized:monitor:entryList,waitList
- ReentrantLock:CLH队列
区别点:
基于AQS vs 基于Monitor
Java生态 vs 非Java生态
- Synchronized是非Java的实现
响应线程中断(InterruptException) vs 不响应
提供tryLock vs 不提供
跨Block vs 单Block
- Synchronized只是一个大括号,是单Block
- ReentrantLock可以在任何地方触发
可配置公平性 vs 不可配置
Semaphore 信号量
Semaphore的作用
控制并发量
具体的作用
可以实现生产者消费者
ArraryBlockingQueue为什么不用Semaphore实现
条件变量性能更高(Condition),Semaphore本身是用来控制并发量而不是进程间的协作,所以条件变量性能更好
那Semaphore的价值是什么
- 抽象价值:降低心智负担
- 其他场景:比如流量控制
CyclicBarrier
Barrier叫做屏障,是一种非常重要的同步元语。 Java提供了多种屏障能力,比如CyclicBarrier让线程在一个屏障上等待,然后执行同步快,然后分开执行,然后进入下一个屏障。
实例场景
假设有1000w个订单要处理,每个订单数据要获取关联的商品和发货单进行分析。
可以考虑每代CyclicBarrier处理1W条订单数据,开10个线程,每个获取1000个商品信息,再开10个线程,每个获取1000个发货单信息,然后利用CyclicBarrier进行一次同步计算。
CyclicBarrier解决了什么问题?
CyclicBarrier提供了一套协作机制,解决的是多个线程间协作(也可以认为是通信),一起处理任务的问题。如果不提供CyclicBarrier我们可以用ReentrantLock+Condition实现类似的能力,但是CyclicBarrier覆盖的场景确实具有通用性,因此抽象成数据结构非常有价值。
CountDownLatch
CountDownLatch的逻辑简单一些,本质上也是一个Barrier,相当于只有一代CyclicBarrier。比如说实现一个返回首页数据的服务,需要请求多个的微服务——A、B、C、D的数据。这个时候可以用CountDownLatch来形成一个同步点。
Phaser
Phaser提供的也是屏障能力,可以把Phaser理解成一种实现屏障能力的框架。 可以用来实现CyclicBarrier和CountDownLatch。后面我们学习TaskForkPoll的时候,也会用到Phaser去实现。
作为框架,最重要的就是思考清楚Barrier这个领域有哪些领域知识,下面我们来总结一下:
屏障(Barrier):合作的线程们在屏障上等待, 然后进入同步点
同步点(Synchronization Point),通过屏障后执行的同步代码
合作方数量(paties),就是互相等待的线程数量,只有当等待数量达到parties,才会进入同步点
到来(arrive),代表一个线程到达屏障,并等待,每次有线程到来,arrives + 1
到达数量(arrives),带到的线程数量
等待(wait),代表线程在barrier上等待
进步(advance),一个线程通过屏障,称为进步,代表工作有进度
开动/下一阶段(tripping/next pharse):到来的线程数量=parties,开始进入同步点
阶段(phase number):类似CyclicBarrier中的代,每次完成一次开动,phase number加1
Phaser除了抽象出了上面这些概念,还提供了一个更灵活的能力,让parties
可以随时变更。一个线程可以声明自己是一个合作方。
考虑对单个线程,Phaser可以从这几个方面思考:
- arrive (到达),在屏障上等待其他合作方, 到达线程数(arrives)增1
- register(注册),相当于声明自己是一个合作方,将
parties
增1 - waitAdvance(等待进步),在屏障上等待其他线程,数量够了就进入同步点
- deregister(注销),相当于注销自己,
parties
减1
以上操作有一些是放在一起用的,比如说arriveAndWaitAdvance
方法,相当于增加了phaser number,又同时等待进步。比如说arriveAndDeregister
相当于,到来,但是不等待,并注销自己。
Exchanger
Exchanger帮助我们在两个线程间交换数据。交换数据本身不是一个原子操作。比如交换a和b,需要一个临时变量t:
1 | t = a; |
上面的程序不是一条原子操作,因此线程间如果要交换数据,需要提供一个专门的能力。
同步器解决了什么问题?
其实核心就是一个问题,同步器提供基于同步的线程间协作。 有交换数据,有互相等待,有控制并发量等等。