本文采用知识共享 署名-相同方式共享 4.0 国际 许可协议进行许可。
访问 https://creativecommons.org/licenses/by-sa/4.0/ 查看该许可协议。

理论上来说,Synchronized 是自动锁,Java 中还有一些手动锁,即本文主题,介绍一些常用锁。
手动锁要注意写在 finally 中,避免死锁发生。

锁种类大致有乐观锁、悲观锁、自旋锁、读写锁、(排它锁、共享锁、)分段锁等等

1) ReentrantLock (重入锁)

众所周知 Synchronized 是可重入的,有一个可以替代 Synchronized 的锁即 ReentrantLock,且提供更多 Synchronized 无法实现的功能。

重入即:有 AB 两方法,需使用同一把锁,线程嵌套调用两方法是否会死锁。

1.1) 常用 API

  1. ReentrantLock(boolean fair):是否 fair,队列确保公平,默认非公平
  2. lock/unlock: 获取锁 / 解锁
  3. boolean trylock:尝试获取锁(可指定 try 的时间),返回是否成功
  4. 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 (倒计数门闩)

等待计数器归零,再继续运行程序

  1. CountDownLatch(count)
  2. await:线程进入阻塞,等待倒计数结束,可选指定超时时间
  3. countDown:count 计数减一
  4. getCount

3) CyclicBarrier (循环屏障)

循环设置一个线程数屏障,当线程数达到屏障数时,放行这些线程

  1. CyclicBarrier:设置屏障,指定屏障计数,可选指定每次打开屏障执行一个线程。
  2. await: 线程进入阻塞,等待屏障打开,可指定超时时间
  3. getNumberWaiting:正在等待线程数
  4. getParties:获取构造方法计数数量
  5. isBroken
  6. 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 阻塞线程和唤醒线程