本文采用知识共享 署名-相同方式共享 4.0 国际 许可协议进行许可。
访问 https://creativecommons.org/licenses/by-sa/4.0/ 查看该许可协议。
理论上来说,Synchronized 是自动锁,Java 中还有一些手动锁,即本文主题,介绍一些常用锁。
手动锁要注意写在 finally 中,避免死锁发生。
锁种类大致有乐观锁、悲观锁、自旋锁、读写锁、(排它锁、共享锁、)分段锁等等
- 1) ReentrantLock (重入锁)
- 2) CountDownLatch (倒计数门闩)
- 3) CyclicBarrier (循环屏障)
- 4) Phase
- 5) ReadWriteLock、StampedLock
- 6) Semaphore (信号量)
- 7) Exchanger
- 8) LockSupport
1) ReentrantLock (重入锁)
众所周知 Synchronized 是可重入的,有一个可以替代 Synchronized 的锁即 ReentrantLock,且提供更多 Synchronized 无法实现的功能。
重入即:有 AB 两方法,需使用同一把锁,线程嵌套调用两方法是否会死锁。
1.1) 常用 API
- ReentrantLock(boolean fair):是否 fair,队列确保公平,默认非公平
- lock/unlock: 获取锁 / 解锁
- boolean trylock:尝试获取锁(可指定 try 的时间),返回是否成功
- lockInterruptibly:获取锁,可对 interrupt 响应
Thread interrupt = new Thread(() -> { try { lock.lockInterruptibly(); // 获取锁 } catch (InterruptedException e) { // ...... 收到 interrupt 信号处理 } finally { // 要注意没拿到锁 unlock 会抛异常 if (lock.isHeldByCurrentThread()) lock.unlock(); } }); interrupt.start(); interrupt.interrupt();
1.2) 对比 Synchronized 优点
- 基于 CAS
- trylock 尝试获取锁
- lockInterruptibly 可打断
- fair 公平锁
2) CountDownLatch (倒计数门闩)
等待计数器归零,再继续运行程序
- CountDownLatch(count)
- await:线程进入阻塞,等待倒计数结束,可选指定超时时间
- countDown:count 计数减一
- getCount
3) CyclicBarrier (循环屏障)
循环设置一个线程数屏障,当线程数达到屏障数时,放行这些线程
- CyclicBarrier:设置屏障,指定屏障计数,可选指定每次打开屏障执行一个线程。
- await: 线程进入阻塞,等待屏障打开,可指定超时时间
- getNumberWaiting:正在等待线程数
- getParties:获取构造方法计数数量
- isBroken
- reset
4) Phase
大致是 CountDownLatch 和 CyclicBarrier 的整合, 可用来实现遗传算法,重写 Phase 自定义每个阶段操作
5) ReadWriteLock、StampedLock
内部有两把锁,读锁共享锁,写锁互斥锁。
读写互斥(排他),读锁被使用时其他读线程可共享这把锁。
支持 Condition
- ReentrantReadWriteLock(fair):是否公平,默认不公平
- boolean trylock:尝试获取锁(可指定 try 的时间),返回是否成功
- lockInterruptibly:获取锁,可对 interrupt 响应
- newCondition
StampedLock 是 ReadWriteLock 的升级态,这里就不贴 API 了可以对照着使用。
6) Semaphore (信号量)
限制同时运行的线程数,可用于限流
- Semaphore: 可指定是否公平
- acquire / acquireUnInterraptibly:获取许可,为获取到许可则等待,默认为可 interrapter
- release: 释放许可
- tryAcquire:尝试获取许可,可指定超时
7) Exchanger
用于两个线程间通信交换变量,例:游戏交换装备
- Exchanger
- exchange(obj):线程进入等待,当有两个线程同时调用了 exchange,交换这两个线程传入对象的引用
8) LockSupport
线程阻塞工具,可以在线程内任意位置阻塞,常用 API 即 park / unpark 阻塞线程和唤醒线程