找回密码
 立即注册
首页 业界区 业界 Java并发编程之Lock锁机制深度解析:从使用到源码实现 ...

Java并发编程之Lock锁机制深度解析:从使用到源码实现

处匈跑 前天 10:05
1. 锁的基本概念:从现实世界到代码世界

1.1 锁的演进:synchronized → Lock

想象一下健身房储物柜的使用场景:

  • synchronized:像固定密码锁 - 简单易用但功能有限
  • Lock接口:像智能电子锁 - 功能丰富且灵活可控
  1. // synchronized - 固定密码锁
  2. public synchronized void oldMethod() {
  3.     // 自动上锁和解锁
  4.     // 但无法中断、无法超时、无法尝试获取
  5. }
  6. // Lock - 智能电子锁  
  7. public void newMethod() {
  8.     Lock lock = new ReentrantLock();
  9.     lock.lock();  // 手动开锁
  10.     try {
  11.         // 临界区代码
  12.     } finally {
  13.         lock.unlock();  // 手动关锁
  14.     }
  15. }
复制代码
1.2 Lock接口的核心优势

特性synchronizedLock中断响应❌✅超时控制❌✅尝试获取❌✅公平性❌✅条件队列单个多个2. AQS:并发世界的交通指挥中心

2.1 AQS的核心设计思想

AQS(AbstractQueuedSynchronizer)就像高速公路收费站系统

  • state状态:当前可通行的车道数量
  • 同步队列:等待通行的车辆排队
  • CAS操作:智能的车辆调度系统
  1. /**
  2. * AQS同步状态管理示例
  3. */
  4. public class AQSCoreConcept {
  5.     // state字段的三种典型用法:
  6.    
  7.     // 1. 互斥锁:state = 0(未锁定) 或 1(已锁定)
  8.     // 2. 重入锁:state = 重入次数  
  9.     // 3. 读写锁:高16位 = 读锁计数,低16位 = 写锁计数
  10.     private volatile int state;
  11.    
  12.     // 三个核心的state操作方法:
  13.     protected final int getState() { return state; }
  14.     protected final void setState(int newState) { state = newState; }
  15.     protected final boolean compareAndSetState(int expect, int update) {
  16.         // CAS原子操作,保证线程安全
  17.         return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
  18.     }
  19. }
复制代码
2.2 同步队列:线程的"等候区"
  1. /**
  2. * AQS同步队列结构演示
  3. */
  4. public class SyncQueueDemo {
  5.     /**
  6.      * 同步队列节点结构(双向链表):
  7.      *
  8.      * head (虚拟节点) ↔ [prev|thread|next|waitStatus] ↔ [prev|thread|next|waitStatus] ↔ tail
  9.      *
  10.      * waitStatus状态说明:
  11.      * - CANCELLED(1):线程已取消
  12.      * - SIGNAL(-1):后继线程需要被唤醒  
  13.      * - CONDITION(-2):线程在Condition队列中
  14.      * - PROPAGATE(-3):共享模式下传播唤醒
  15.      */
  16.    
  17.     // 独占模式获取锁的典型流程
  18.     public void acquireDemo() {
  19.         Lock lock = new ReentrantLock();
  20.         
  21.         // 底层调用AQS的acquire方法
  22.         lock.lock();  // -> sync.acquire(1);
  23.         
  24.         /**
  25.          * acquire方法执行流程:
  26.          * 1. tryAcquire()尝试直接获取锁
  27.          * 2. 失败 → addWaiter()加入同步队列队尾
  28.          * 3. acquireQueued()在队列中自旋等待
  29.          * 4. 被前驱节点唤醒后重新尝试获取锁
  30.          */
  31.     }
  32. }
复制代码
2.3 自定义锁实战:基于AQS实现TwinsLock
  1. /**
  2. * TwinsLock - 同一时刻最多允许两个线程访问的共享锁
  3. * 设计思路:将AQS的state作为许可证计数器
  4. */
  5. public class TwinsLock implements Lock {
  6.     private final Sync sync = new Sync(2);
  7.    
  8.     private static final class Sync extends AbstractQueuedSynchronizer {
  9.         Sync(int count) {
  10.             if (count <= 0) throw new IllegalArgumentException("计数必须大于0");
  11.             setState(count);  // 初始化许可证数量
  12.         }
  13.         
  14.         /**
  15.          * 共享模式获取锁
  16.          * @return 负数:获取失败;0:获取成功但无剩余;正数:获取成功且有剩余
  17.          */
  18.         @Override
  19.         public int tryAcquireShared(int reduceCount) {
  20.             for (;;) {  // 自旋避免CAS失败
  21.                 int current = getState();
  22.                 int newCount = current - reduceCount;
  23.                
  24.                 // 如果新计数<0(无许可证)或CAS设置成功,返回结果
  25.                 if (newCount < 0 || compareAndSetState(current, newCount)) {
  26.                     return newCount;
  27.                 }
  28.             }
  29.         }
  30.         
  31.         /**
  32.          * 共享模式释放锁
  33.          */
  34.         @Override
  35.         public boolean tryReleaseShared(int returnCount) {
  36.             for (;;) {
  37.                 int current = getState();
  38.                 int newCount = current + returnCount;
  39.                 if (compareAndSetState(current, newCount)) {
  40.                     return true;
  41.                 }
  42.             }
  43.         }
  44.     }
  45.    
  46.     @Override
  47.     public void lock() {
  48.         sync.acquireShared(1);  // 获取1个许可证
  49.     }
  50.    
  51.     @Override
  52.     public void unlock() {
  53.         sync.releaseShared(1);  // 释放1个许可证
  54.     }
  55.    
  56.     // 其他Lock方法实现...
  57. }
  58. /**
  59. * 测试TwinsLock
  60. */
  61. public class TwinsLockTest {
  62.     @Test
  63.     public void testTwinsLock() {
  64.         final Lock lock = new TwinsLock();
  65.         
  66.         // 启动10个线程,但同一时刻只有2个能获取锁
  67.         for (int i = 0; i < 10; i++) {
  68.             Thread worker = new Thread(() -> {
  69.                 lock.lock();
  70.                 try {
  71.                     System.out.println(Thread.currentThread().getName() + " 获取锁");
  72.                     Thread.sleep(1000);
  73.                 } catch (InterruptedException e) {
  74.                     Thread.currentThread().interrupt();
  75.                 } finally {
  76.                     lock.unlock();
  77.                 }
  78.             });
  79.             worker.start();
  80.         }
  81.     }
  82. }
复制代码
5.2 许可机制:先发后至的灵活性
  1. /**
  2. * 重入锁的重入特性演示
  3. */
  4. public class ReentrantDemo {
  5.     private final ReentrantLock lock = new ReentrantLock();
  6.    
  7.     public void outer() {
  8.         lock.lock();
  9.         try {
  10.             System.out.println("外层方法获取锁,重入计数: " + getHoldCount());
  11.             inner();  // 重入:同一个线程再次获取同一把锁
  12.         } finally {
  13.             lock.unlock();
  14.         }
  15.     }
  16.    
  17.     public void inner() {
  18.         lock.lock();  // 这里不会阻塞,因为已经是锁的持有者
  19.         try {
  20.             System.out.println("内层方法获取锁,重入计数: " + getHoldCount());
  21.         } finally {
  22.             lock.unlock();
  23.         }
  24.     }
  25.    
  26.     private int getHoldCount() {
  27.         // 返回当前线程持有该锁的次数
  28.         return lock.getHoldCount();
  29.     }
  30. }
复制代码
5.3 Blocker:线程诊断的"身份证"
  1. /**
  2. * 公平性对比测试
  3. */
  4. public class FairVsUnfairTest {
  5.     private static Lock fairLock = new ReentrantLock(true);      // 公平锁
  6.     private static Lock unfairLock = new ReentrantLock(false);   // 非公平锁
  7.    
  8.     @Test
  9.     public void comparePerformance() {
  10.         // 测试结果通常显示:
  11.         // - 公平锁:保证顺序,但性能较低
  12.         // - 非公平锁:可能饥饿,但吞吐量高
  13.         
  14.         testLock("公平锁", fairLock);
  15.         testLock("非公平锁", unfairLock);
  16.     }
  17.    
  18.     private void testLock(String type, Lock lock) {
  19.         long start = System.currentTimeMillis();
  20.         
  21.         // 多个线程竞争锁...
  22.         
  23.         long duration = System.currentTimeMillis() - start;
  24.         System.out.println(type + " 耗时: " + duration + "ms");
  25.     }
  26. }
复制代码
6. Condition接口:精准的线程协调器

6.1 Condition vs Object监视器

特性Object.wait/notifyCondition.await/signal前置条件必须在synchronized内必须先获取Lock等待队列一个对象一个队列一个Lock多个Condition精确通知只能notifyAll或随机可以精确通知特定Condition超时控制有限支持丰富的时间控制方法6.2 Condition实战:有界阻塞队列
  1. /**
  2. * 重入锁核心实现解析
  3. */
  4. public class ReentrantLockCore {
  5.     /**
  6.      * 非公平锁获取逻辑
  7.      */
  8.     final boolean nonfairTryAcquire(int acquires) {
  9.         final Thread current = Thread.currentThread();
  10.         int c = getState();
  11.         
  12.         if (c == 0) {  // 锁空闲
  13.             if (compareAndSetState(0, acquires)) {
  14.                 setExclusiveOwnerThread(current);
  15.                 return true;
  16.             }
  17.         } else if (current == getExclusiveOwnerThread()) {  // 重入
  18.             int nextc = c + acquires;
  19.             if (nextc < 0) throw new Error("超过最大锁计数");
  20.             setState(nextc);  // 增加重入计数
  21.             return true;
  22.         }
  23.         return false;
  24.     }
  25.    
  26.     /**
  27.      * 释放锁逻辑
  28.      */
  29.     protected final boolean tryRelease(int releases) {
  30.         int c = getState() - releases;
  31.         if (Thread.currentThread() != getExclusiveOwnerThread())
  32.             throw new IllegalMonitorStateException();
  33.             
  34.         boolean free = false;
  35.         if (c == 0) {  // 完全释放
  36.             free = true;
  37.             setExclusiveOwnerThread(null);
  38.         }
  39.         setState(c);
  40.         return free;
  41.     }
  42. }
复制代码
8. 实战指南:如何正确使用Java并发锁

8.1 锁使用的核心原则

原则1:永远在finally块中释放锁
  1. /**
  2. * 基于读写锁的缓存实现
  3. */
  4. public class ReadWriteCache<K, V> {
  5.     private final Map<K, V> cache = new HashMap<>();
  6.     private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
  7.     private final Lock readLock = rwLock.readLock();
  8.     private final Lock writeLock = rwLock.writeLock();
  9.    
  10.     /**
  11.      * 读操作:共享锁,允许多个线程同时读
  12.      */
  13.     public V get(K key) {
  14.         readLock.lock();
  15.         try {
  16.             String value = cache.get(key);
  17.             // 模拟配置读取的耗时操作
  18.             simulateProcess(1);
  19.             return value;
  20.         } finally {
  21.             readLock.unlock();
  22.         }
  23.     }
  24.    
  25.     /**
  26.      * 批量获取配置 - 读锁支持并发
  27.      */
  28.     public Map<String, String> getConfigs(Set<String> keys) {
  29.         readLock.lock();
  30.         try {
  31.             Map<String, String> result = new HashMap<>();
  32.             for (String key : keys) {
  33.                 result.put(key, cache.get(key));
  34.             }
  35.             simulateProcess(keys.size());
  36.             return result;
  37.         } finally {
  38.             readLock.unlock();
  39.         }
  40.     }
  41.    
  42.     /**
  43.      * 更新配置 - 低频操作,使用写锁
  44.      */
  45.     public void updateConfig(String key, String value) {
  46.         writeLock.lock();
  47.         try {
  48.             // 模拟配置更新的耗时操作
  49.             simulateProcess(10);
  50.             cache.put(key, value);
  51.             System.out.println("配置更新: " + key + " = " + value);
  52.         } finally {
  53.             writeLock.unlock();
  54.         }
  55.     }
  56.    
  57.     private void simulateProcess(int milliseconds) {
  58.         try {
  59.             Thread.sleep(milliseconds);
  60.         } catch (InterruptedException e) {
  61.             Thread.currentThread().interrupt();
  62.         }
  63.     }
  64. }
复制代码
原则2:避免锁嵌套,预防死锁
  1. /**
  2. * 读写锁状态设计的精妙之处
  3. */
  4. public class ReadWriteStateDesign {
  5.     /**
  6.      * 32位state字段的划分:
  7.      *
  8.      * ┌─────────────────┬─────────────────┐
  9.      * │     高16位      │     低16位      │
  10.      * │     读状态      │     写状态      │
  11.      * │   (读锁计数)    │  (写锁重入数)   │
  12.      * └─────────────────┴─────────────────┘
  13.      */
  14.    
  15.     // 获取写状态(低16位)
  16.     static int exclusiveCount(int c) {
  17.         return c & 0x0000FFFF;
  18.     }
  19.    
  20.     // 获取读状态(高16位)
  21.     static int sharedCount(int c) {
  22.         return c >>> 16;
  23.     }
  24.    
  25.     // 读锁计数+1
  26.     int newReadState = currentState + (1 << 16);  // 即 + 0x00010000
  27.    
  28.     // 写锁计数+1  
  29.     int newWriteState = currentState + 1;
  30. }
复制代码
8.2 各组件最佳实践案例

8.2.1 ReentrantLock最佳实践:连接池管理
  1. /**
  2. * 锁降级示例:写锁 → 读锁
  3. * 目的:保证数据的可见性
  4. */
  5. public class LockDemotionExample {
  6.     private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
  7.     private final Lock readLock = rwLock.readLock();
  8.     private final Lock writeLock = rwLock.writeLock();
  9.     private volatile boolean update = false;
  10.     private Object data;
  11.    
  12.     public void processData() {
  13.         readLock.lock();
  14.         if (!update) {
  15.             // 数据需要更新,必须先释放读锁
  16.             readLock.unlock();
  17.             
  18.             // 获取写锁
  19.             writeLock.lock();
  20.             try {
  21.                 // 双重检查
  22.                 if (!update) {
  23.                     // 准备数据...
  24.                     data = fetchData();
  25.                     update = true;
  26.                 }
  27.                 // 关键步骤:在释放写锁前获取读锁
  28.                 readLock.lock();  // 锁降级开始
  29.             } finally {
  30.                 writeLock.unlock();  // 锁降级完成,现在持有读锁
  31.             }
  32.         }
  33.         
  34.         try {
  35.             // 使用数据(仍在读锁保护下)
  36.             useData(data);
  37.         } finally {
  38.             readLock.unlock();
  39.         }
  40.     }
  41.    
  42.     // 不支持锁升级!可能产生死锁
  43.     public void invalidLockUpgrade() {
  44.         readLock.lock();
  45.         try {
  46.             // 危险操作:尝试在持有读锁时获取写锁
  47.             // 如果其他线程也持有读锁,会产生死锁
  48.             writeLock.lock();  // 可能永远阻塞!
  49.             try {
  50.                 // 修改数据...
  51.             } finally {
  52.                 writeLock.unlock();
  53.             }
  54.         } finally {
  55.             readLock.unlock();
  56.         }
  57.     }
  58.    
  59.     private Object fetchData() { return null; }
  60.     private void useData(Object data) {}
  61. }
复制代码
8.2.4 LockSupport最佳实践:自定义同步器
  1. /**
  2. * LockSupport基础使用
  3. */
  4. public class LockSupportBasic {
  5.    
  6.     public static void main(String[] args) throws InterruptedException {
  7.         Thread worker = new Thread(() -> {
  8.             System.out.println("工作线程开始执行");
  9.             System.out.println("工作线程即将被阻塞");
  10.             
  11.             // 阻塞当前线程(停车)
  12.             LockSupport.park();
  13.             
  14.             System.out.println("工作线程被唤醒,继续执行");
  15.         });
  16.         
  17.         worker.start();
  18.         
  19.         Thread.sleep(2000);  // 主线程等待2秒
  20.         
  21.         System.out.println("主线程准备唤醒工作线程");
  22.         
  23.         // 唤醒指定线程(开车)
  24.         LockSupport.unpark(worker);
  25.         
  26.         System.out.println("主线程已发送唤醒信号");
  27.     }
  28. }
复制代码
总结:正确使用并发锁的黄金法则


  • 明确性:清楚知道每个锁保护的是什么数据
  • 最小化:锁粒度尽可能小,持有时间尽可能短
  • 一致性:按照固定顺序获取多个锁
  • 可靠性:总是在finally块中释放锁
  • 可中断性:对长时间操作使用可中断的锁获取方式
  • 监控性:对关键锁进行监控和统计
Java并发包中的锁机制通过精妙的设计实现了高效、灵活的线程同步:

  • AQS是基石:提供了同步队列管理和状态控制的基础设施
  • Lock接口是门面:定义了丰富的锁操作API
  • 重入锁解决重入问题:支持同一线程多次获取锁
  • 读写锁优化读多写少场景:通过锁分离提升并发性能
  • LockSupport提供底层阻塞能力:线程控制的精准工具
  • Condition实现精确线程协调:多条件等待队列支持复杂同步逻辑
记住这个核心关系:Lock → AQS → Condition → LockSupport,它们共同构成了Java并发锁机制的完整体系。理解这些组件的内部机制,能够帮助我们正确选择和使用合适的同步工具,诊断和解决复杂的并发问题,设计高性能的并发组件。
记住这些原则和最佳实践,你就能构建出高效、可靠的并发程序。并发编程虽然复杂,但通过正确的工具和方法,完全可以驾驭!

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

昨天 09:43

举报

新版吗?好像是停更了吧。
您需要登录后才可以回帖 登录 | 立即注册