冬眠的笔记
首页文章分类书单项目关于
冬眠
X

© 2026 冬眠的笔记 · 用文字记录思考,用思考改变生活

首页>文章>Java
JavaJUC锁并发

独占锁和共享锁

AQS 独占模式与共享模式的区别、典型实现(ReentrantLock vs ReadWriteLock)以及使用场景

冬眠
冬眠
专注于技术、阅读与思考
2025-11-19
发布日期
32 min read
阅读时长
浏览量
独占锁和共享锁

一、概述

9.3 应用场景问题

Q6: 在什么情况下选择Semaphore而不是其他锁?

答案: Semaphore适用于以下场景:

  1. 资源池管理:如数据库连接池、线程池
  2. 限流控制:限制同时访问某个资源的线程数量
  3. 生产者消费者:控制缓冲区的容量
  4. 并发度控制:精确控制并发访问的数量
// 示例:限制同时下载的线程数
public class DownloadManager {
    private final Semaphore downloadSemaphore = new Semaphore(3); // 最多3个并发下载
    
    public void download(String url) {
        try {
            downloadSemaphore.acquire();
            // 执行下载操作
            performDownload(url);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            downloadSemaphore.release();
        }
    }
}

Q7: CountDownLatch和CyclicBarrier的区别?

答案:

特性 CountDownLatch CyclicBarrier
复用性 一次性使用 可以重复使用
等待方式 一个或多个线程等待其他线程完成 所有线程相互等待
计数方向 递减到0 递增到指定值
触发条件 计数为0时释放等待线程 所有线程到达屏障点
使用场景 主线程等待子线程完成 多线程同步执行

9.4 性能优化问题

Q8: 如何减少锁竞争?

答案:

  1. 减少锁的粒度:使用分段锁、细粒度锁
  2. 减少锁的持有时间:将耗时操作移到锁外
  3. 使用读写锁:读多写少场景使用ReadWriteLock
  4. 使用无锁数据结构:如ConcurrentHashMap、AtomicInteger
  5. 锁分离:将读锁和写锁分离
  6. 避免锁嵌套:减少死锁风险

Q9: 什么是锁消除和锁粗化?

答案:

锁消除(Lock Elimination): JVM在编译时或运行时发现某些锁操作不可能存在竞争,会自动消除这些锁。

// JVM可能会消除这个锁,因为sb是局部变量
public String concatString(String s1, String s2) {
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    return sb.toString();
}

锁粗化(Lock Coarsening): JVM将多个连续的锁操作合并为一个更大范围的锁操作。

// 原始代码:多次加锁
for (int i = 0; i < 1000; i++) {
    synchronized(obj) {
        // 操作
    }
}

// 优化后:锁粗化
synchronized(obj) {
    for (int i = 0; i < 1000; i++) {
        // 操作
    }
}

9.5 实战问题

Q10: 如何实现一个支持超时的共享锁?

答案:

public class TimeoutSharedLock {
    private final Semaphore semaphore;
    
    public TimeoutSharedLock(int permits) {
        this.semaphore = new Semaphore(permits);
    }
    
    public boolean tryAcquire(long timeout, TimeUnit unit) {
        try {
            return semaphore.tryAcquire(timeout, unit);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
    }
    
    public void release() {
        semaphore.release();
    }
    
    public int availablePermits() {
        return semaphore.availablePermits();
    }
}

Q11: 如何避免读写锁的写饥饿问题?

答案: 写饥饿问题是指在读操作频繁的情况下,写操作可能长时间无法获得锁。解决方案:

  1. 使用公平锁:
ReadWriteLock fairLock = new ReentrantReadWriteLock(true);
  1. 限制读锁的获取:
public class WriterPreferenceReadWriteLock {
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final AtomicInteger waitingWriters = new AtomicInteger(0);
    
    public void readLock() {
        // 如果有等待的写线程,读线程等待
        while (waitingWriters.get() > 0) {
            Thread.yield();
        }
        rwLock.readLock().lock();
    }
    
    public void writeLock() {
        waitingWriters.incrementAndGet();
        try {
            rwLock.writeLock().lock();
        } finally {
            waitingWriters.decrementAndGet();
        }
    }
}
  1. 使用StampedLock:
public class StampedLockExample {
    private final StampedLock sl = new StampedLock();
    private double x, y;
    
    void write(double newX, double newY) {
        long stamp = sl.writeLock();
        try {
            x = newX;
            y = newY;
        } finally {
            sl.unlockWrite(stamp);
        }
    }
    
    double distanceFromOrigin() {
        long stamp = sl.tryOptimisticRead();
        double curX = x, curY = y;
        if (!sl.validate(stamp)) {
            stamp = sl.readLock();
            try {
                curX = x;
                curY = y;
            } finally {
                sl.unlockRead(stamp);
            }
        }
        return Math.sqrt(curX * curX + curY * curY);
    }
}

总结

核心要点回顾

  1. 独占锁特点:

    • 同时只允许一个线程持有
    • 适用于写操作和数据修改
    • 安全性高,并发度低
    • 典型实现:synchronized、ReentrantLock
  2. 共享锁特点:

    • 允许多个线程同时持有
    • 适用于读操作和数据查询
    • 并发度高,读性能好
    • 典型实现:ReadWriteLock、Semaphore
  3. 选择原则:

    • 读多写少:选择读写锁
    • 需要超时/中断:选择ReentrantLock
    • 简单互斥:选择synchronized
    • 控制并发数:选择Semaphore
    • 等待协调:选择CountDownLatch/CyclicBarrier
  4. 性能优化:

    • 减少锁粒度和持有时间
    • 使用合适的锁类型
    • 避免锁竞争和死锁
    • 考虑使用无锁数据结构
  5. 最佳实践:

    • 始终在finally块中释放锁
    • 避免在锁内进行耗时操作
    • 按固定顺序获取多个锁
    • 合理选择公平锁和非公平锁

发展趋势

随着Java并发编程的发展,出现了更多高性能的同步工具:

  • StampedLock:提供乐观读锁,进一步提升读性能
  • CompletableFuture:异步编程模型,减少线程阻塞
  • Reactive Streams:响应式编程,处理高并发场景
  • Virtual Threads:轻量级线程,降低线程创建成本

理解独占锁和共享锁的区别和应用场景,是掌握Java并发编程的重要基础。在实际开发中,应该根据具体的业务需求和性能要求,选择合适的同步机制,并遵循最佳实践,编写高效、安全的并发代码。

1.1 基本定义

独占锁(Exclusive Lock):也称为排他锁、写锁,同一时刻只能有一个线程持有锁,其他线程必须等待。

共享锁(Shared Lock):也称为读锁,同一时刻可以有多个线程同时持有锁,但不能与独占锁同时存在。

1.2 锁的分类体系

锁的分类
├── 按访问性质分类
│   ├── 独占锁(排他锁)
│   │   ├── ReentrantLock
│   │   ├── synchronized
│   │   └── WriteLock
│   └── 共享锁
│       ├── ReadLock
│       ├── Semaphore
│       └── CountDownLatch
├── 按公平性分类
│   ├── 公平锁
│   └── 非公平锁
└── 按可重入性分类
    ├── 可重入锁
    └── 不可重入锁

1.3 应用场景概览

锁类型 适用场景 典型实现 性能特点
独占锁 写操作、临界资源修改 ReentrantLock、synchronized 安全性高,并发度低
共享锁 读操作、数据查询 ReadWriteLock.readLock() 并发度高,读性能好

二、独占锁详解

2.1 独占锁的特点

/**
 * 独占锁的核心特点:
 * 1. 互斥性:同一时刻只有一个线程能持有锁
 * 2. 排他性:持有锁的线程可以读写,其他线程被阻塞
 * 3. 安全性:保证数据的一致性和完整性
 */
public class ExclusiveLockDemo {
    private final ReentrantLock lock = new ReentrantLock();
    private int count = 0;
    
    public void increment() {
        lock.lock();
        try {
            // 只有一个线程能执行这里的代码
            count++;
            System.out.println(Thread.currentThread().getName() + 
                             " incremented count to: " + count);
        } finally {
            lock.unlock();
        }
    }
    
    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}

2.2 synchronized关键字

/**
 * synchronized是Java内置的独占锁
 */
public class SynchronizedDemo {
    private int count = 0;
    
    // 方法级别的synchronized
    public synchronized void increment() {
        count++;
    }
    
    // 代码块级别的synchronized
    public void decrement() {
        synchronized(this) {
            count--;
        }
    }
    
    // 静态方法的synchronized(类锁)
    public static synchronized void staticMethod() {
        // 锁定的是Class对象
    }
}

2.3 ReentrantLock详解

/**
 * ReentrantLock是可重入的独占锁
 */
public class ReentrantLockDemo {
    private final ReentrantLock lock = new ReentrantLock();
    
    public void method1() {
        lock.lock();
        try {
            System.out.println("Method1 acquired lock");
            method2(); // 可重入:同一线程可以再次获取锁
        } finally {
            lock.unlock();
        }
    }
    
    public void method2() {
        lock.lock();
        try {
            System.out.println("Method2 acquired lock");
            // 执行业务逻辑
        } finally {
            lock.unlock();
        }
    }
    
    // 尝试获取锁(非阻塞)
    public boolean tryLockExample() {
        if (lock.tryLock()) {
            try {
                // 获取锁成功,执行业务逻辑
                return true;
            } finally {
                lock.unlock();
            }
        } else {
            // 获取锁失败,执行其他逻辑
            return false;
        }
    }
    
    // 超时获取锁
    public boolean tryLockWithTimeout() throws InterruptedException {
        if (lock.tryLock(5, TimeUnit.SECONDS)) {
            try {
                // 在5秒内获取到锁
                return true;
            } finally {
                lock.unlock();
            }
        } else {
            // 5秒内未获取到锁
            return false;
        }
    }
}

2.4 独占锁的AQS实现

/**
 * 独占锁在AQS中的实现原理
 */
public class ExclusiveLockAQSAnalysis {
    /*
     * AQS中独占锁的核心方法:
     * 
     * 1. tryAcquire(int arg):尝试获取锁
     *    - 返回true表示获取成功
     *    - 返回false表示获取失败,需要排队
     * 
     * 2. tryRelease(int arg):尝试释放锁
     *    - 返回true表示完全释放
     *    - 返回false表示部分释放(重入锁场景)
     * 
     * 3. isHeldExclusively():判断是否独占持有
     */
    
    // ReentrantLock中的实现示例
    static final class NonfairSync extends AbstractQueuedSynchronizer {
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                // 锁未被占用,尝试获取
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                // 重入逻辑
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
        
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
    }
}

三、共享锁详解

3.1 共享锁的特点

/**
 * 共享锁的核心特点:
 * 1. 共享性:多个线程可以同时持有锁
 * 2. 读优化:适合读多写少的场景
 * 3. 条件限制:不能与独占锁同时存在
 */
public class SharedLockDemo {
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final Lock readLock = rwLock.readLock();
    private final Lock writeLock = rwLock.writeLock();
    private String data = "initial data";
    
    // 读操作:使用共享锁
    public String readData() {
        readLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " reading: " + data);
            // 模拟读操作耗时
            Thread.sleep(1000);
            return data;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return null;
        } finally {
            readLock.unlock();
        }
    }
    
    // 写操作:使用独占锁
    public void writeData(String newData) {
        writeLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " writing: " + newData);
            this.data = newData;
            // 模拟写操作耗时
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            writeLock.unlock();
        }
    }
}

3.2 Semaphore信号量

/**
 * Semaphore是一种共享锁的实现
 * 允许指定数量的线程同时访问资源
 */
public class SemaphoreDemo {
    // 允许3个线程同时访问
    private final Semaphore semaphore = new Semaphore(3);
    
    public void accessResource() {
        try {
            // 获取许可
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() + " acquired permit");
            
            // 模拟资源访问
            Thread.sleep(2000);
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            // 释放许可
            semaphore.release();
            System.out.println(Thread.currentThread().getName() + " released permit");
        }
    }
    
    // 尝试获取多个许可
    public boolean tryAcquireMultiple(int permits) {
        try {
            if (semaphore.tryAcquire(permits, 3, TimeUnit.SECONDS)) {
                System.out.println("Acquired " + permits + " permits");
                return true;
            } else {
                System.out.println("Failed to acquire " + permits + " permits");
                return false;
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
    }
}

3.3 CountDownLatch倒计时门闩

/**
 * CountDownLatch是一种特殊的共享锁
 * 允许一个或多个线程等待其他线程完成操作
 */
public class CountDownLatchDemo {
    private final CountDownLatch startLatch = new CountDownLatch(1);
    private final CountDownLatch endLatch = new CountDownLatch(3);
    
    public void workerThread(int workerId) {
        try {
            // 等待开始信号
            System.out.println("Worker " + workerId + " waiting for start signal");
            startLatch.await();
            
            // 执行工作
            System.out.println("Worker " + workerId + " started working");
            Thread.sleep(2000 + workerId * 1000); // 模拟不同的工作时间
            System.out.println("Worker " + workerId + " finished working");
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            // 完成工作,计数减一
            endLatch.countDown();
        }
    }
    
    public void coordinatorThread() {
        try {
            System.out.println("Coordinator preparing...");
            Thread.sleep(1000);
            
            // 发出开始信号
            System.out.println("Coordinator sending start signal");
            startLatch.countDown();
            
            // 等待所有工作完成
            System.out.println("Coordinator waiting for all workers to finish");
            endLatch.await();
            System.out.println("All workers finished, coordinator proceeding");
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

3.4 共享锁的AQS实现

/**
 * 共享锁在AQS中的实现原理
 */
public class SharedLockAQSAnalysis {
    /*
     * AQS中共享锁的核心方法:
     * 
     * 1. tryAcquireShared(int arg):尝试获取共享锁
     *    - 返回负数表示获取失败
     *    - 返回0表示获取成功但后续获取可能失败
     *    - 返回正数表示获取成功且后续获取也可能成功
     * 
     * 2. tryReleaseShared(int arg):尝试释放共享锁
     *    - 返回true表示释放后可能有等待线程需要唤醒
     *    - 返回false表示释放后无需唤醒等待线程
     */
    
    // Semaphore中的实现示例
    static final class NonfairSync extends AbstractQueuedSynchronizer {
        NonfairSync(int permits) {
            setState(permits);
        }
        
        protected int tryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
        
        protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))
                    return true;
            }
        }
    }
    
    // CountDownLatch中的实现示例
    static final class Sync extends AbstractQueuedSynchronizer {
        Sync(int count) {
            setState(count);
        }
        
        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }
        
        protected boolean tryReleaseShared(int releases) {
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }
}

四、核心区别对比

4.1 功能特性对比

特性 独占锁 共享锁
并发访问 同时只允许一个线程 同时允许多个线程
读写操作 读写都互斥 读读不互斥,读写互斥
性能 并发度低,安全性高 并发度高,读性能好
适用场景 写操作、数据修改 读操作、数据查询
典型实现 synchronized、ReentrantLock ReadWriteLock、Semaphore
AQS方法 tryAcquire/tryRelease tryAcquireShared/tryReleaseShared

4.2 性能对比示例

/**
 * 独占锁 vs 共享锁性能对比
 */
public class LockPerformanceComparison {
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final ReentrantLock exclusiveLock = new ReentrantLock();
    private volatile String data = "test data";
    
    // 使用独占锁的读操作
    public String readWithExclusiveLock() {
        exclusiveLock.lock();
        try {
            // 模拟读操作
            Thread.sleep(10);
            return data;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return null;
        } finally {
            exclusiveLock.unlock();
        }
    }
    
    // 使用共享锁的读操作
    public String readWithSharedLock() {
        rwLock.readLock().lock();
        try {
            // 模拟读操作
            Thread.sleep(10);
            return data;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return null;
        } finally {
            rwLock.readLock().unlock();
        }
    }
    
    // 性能测试
    public static void performanceTest() {
        LockPerformanceComparison test = new LockPerformanceComparison();
        int threadCount = 10;
        int iterations = 1000;
        
        // 测试独占锁
        long startTime = System.currentTimeMillis();
        CountDownLatch latch1 = new CountDownLatch(threadCount);
        for (int i = 0; i < threadCount; i++) {
            new Thread(() -> {
                for (int j = 0; j < iterations; j++) {
                    test.readWithExclusiveLock();
                }
                latch1.countDown();
            }).start();
        }
        
        try {
            latch1.await();
            long exclusiveTime = System.currentTimeMillis() - startTime;
            System.out.println("Exclusive lock time: " + exclusiveTime + "ms");
            
            // 测试共享锁
            startTime = System.currentTimeMillis();
            CountDownLatch latch2 = new CountDownLatch(threadCount);
            for (int i = 0; i < threadCount; i++) {
                new Thread(() -> {
                    for (int j = 0; j < iterations; j++) {
                        test.readWithSharedLock();
                    }
                    latch2.countDown();
                }).start();
            }
            
            latch2.await();
            long sharedTime = System.currentTimeMillis() - startTime;
            System.out.println("Shared lock time: " + sharedTime + "ms");
            System.out.println("Performance improvement: " + 
                             (double)exclusiveTime / sharedTime + "x");
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

4.3 状态转换图

独占锁状态转换:
[未锁定] --acquire--> [已锁定] --release--> [未锁定]
    ↑                      ↓
    └-------- reentrant ----┘

共享锁状态转换:
[未锁定] --acquire--> [共享锁定] --acquire--> [多重共享锁定]
    ↑                      ↓                      ↓
    └-------- release ------┴-------- release ----┘

读写锁状态转换:
[未锁定] ←→ [读锁定] ←→ [多重读锁定]
    ↕
[写锁定]

五、实现原理

5.1 AQS中的实现差异

/**
 * AQS中独占锁和共享锁的实现差异
 */
public class AQSImplementationDifference {
    
    // 独占锁的获取流程
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    
    // 共享锁的获取流程
    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
    
    // 独占锁的释放流程
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h); // 只唤醒一个后继节点
            return true;
        }
        return false;
    }
    
    // 共享锁的释放流程
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared(); // 可能唤醒多个等待节点
            return true;
        }
        return false;
    }
}

5.2 等待队列中的节点类型

/**
 * AQS等待队列中的节点类型
 */
static final class Node {
    // 共享模式标记
    static final Node SHARED = new Node();
    // 独占模式标记
    static final Node EXCLUSIVE = null;
    
    // 下一个等待条件的节点,或者共享模式标记
    Node nextWaiter;
    
    // 判断是否为共享模式
    final boolean isShared() {
        return nextWaiter == SHARED;
    }
}

5.3 唤醒机制的差异

/**
 * 独占锁和共享锁的唤醒机制差异
 */
public class WakeupMechanismDifference {
    
    // 独占锁:只唤醒一个后继节点
    private void unparkSuccessor(Node node) {
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }
    
    // 共享锁:可能唤醒多个等待节点
    private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;
            }
            if (h == head)
                break;
        }
    }
    
    // 共享锁获取成功后的传播机制
    private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head;
        setHead(node);
        
        // 如果还有剩余资源,继续唤醒后继节点
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }
}

六、典型应用场景

6.1 独占锁应用场景

6.1.1 银行转账系统

/**
 * 银行转账系统 - 独占锁保证数据一致性
 */
public class BankTransferSystem {
    private final Map<String, Account> accounts = new ConcurrentHashMap<>();
    private final ReentrantLock transferLock = new ReentrantLock();
    
    public boolean transfer(String fromAccount, String toAccount, double amount) {
        transferLock.lock();
        try {
            Account from = accounts.get(fromAccount);
            Account to = accounts.get(toAccount);
            
            if (from == null || to == null) {
                return false;
            }
            
            if (from.getBalance() < amount) {
                return false;
            }
            
            // 原子性操作:扣款和入账必须同时成功或失败
            from.withdraw(amount);
            to.deposit(amount);
            
            System.out.println("Transfer successful: " + amount + 
                             " from " + fromAccount + " to " + toAccount);
            return true;
            
        } finally {
            transferLock.unlock();
        }
    }
    
    static class Account {
        private double balance;
        private final ReentrantLock lock = new ReentrantLock();
        
        public void withdraw(double amount) {
            lock.lock();
            try {
                balance -= amount;
            } finally {
                lock.unlock();
            }
        }
        
        public void deposit(double amount) {
            lock.lock();
            try {
                balance += amount;
            } finally {
                lock.unlock();
            }
        }
        
        public double getBalance() {
            lock.lock();
            try {
                return balance;
            } finally {
                lock.unlock();
            }
        }
    }
}

6.1.2 单例模式

/**
 * 线程安全的单例模式 - 使用独占锁
 */
public class ThreadSafeSingleton {
    private static volatile ThreadSafeSingleton instance;
    private static final ReentrantLock lock = new ReentrantLock();
    
    private ThreadSafeSingleton() {
        // 私有构造函数
    }
    
    public static ThreadSafeSingleton getInstance() {
        if (instance == null) {
            lock.lock();
            try {
                // 双重检查锁定
                if (instance == null) {
                    instance = new ThreadSafeSingleton();
                }
            } finally {
                lock.unlock();
            }
        }
        return instance;
    }
}

6.2 共享锁应用场景

6.2.1 缓存系统

/**
 * 缓存系统 - 读写锁优化读性能
 */
public class CacheSystem<K, V> {
    private final Map<K, V> cache = new HashMap<>();
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final Lock readLock = rwLock.readLock();
    private final Lock writeLock = rwLock.writeLock();
    
    // 读操作:使用共享锁,多个线程可以并发读取
    public V get(K key) {
        readLock.lock();
        try {
            V value = cache.get(key);
            System.out.println(Thread.currentThread().getName() + 
                             " reading key: " + key + ", value: " + value);
            return value;
        } finally {
            readLock.unlock();
        }
    }
    
    // 写操作:使用独占锁,保证数据一致性
    public void put(K key, V value) {
        writeLock.lock();
        try {
            cache.put(key, value);
            System.out.println(Thread.currentThread().getName() + 
                             " writing key: " + key + ", value: " + value);
        } finally {
            writeLock.unlock();
        }
    }
    
    // 删除操作:使用独占锁
    public V remove(K key) {
        writeLock.lock();
        try {
            V removed = cache.remove(key);
            System.out.println(Thread.currentThread().getName() + 
                             " removed key: " + key);
            return removed;
        } finally {
            writeLock.unlock();
        }
    }
    
    // 获取缓存大小:使用共享锁
    public int size() {
        readLock.lock();
        try {
            return cache.size();
        } finally {
            readLock.unlock();
        }
    }
}

6.2.2 连接池管理

/**
 * 数据库连接池 - 使用信号量控制并发访问
 */
public class DatabaseConnectionPool {
    private final Queue<Connection> connections = new LinkedList<>();
    private final Semaphore semaphore;
    private final int maxConnections;
    
    public DatabaseConnectionPool(int maxConnections) {
        this.maxConnections = maxConnections;
        this.semaphore = new Semaphore(maxConnections);
        
        // 初始化连接池
        for (int i = 0; i < maxConnections; i++) {
            connections.offer(createConnection());
        }
    }
    
    // 获取连接
    public Connection getConnection() throws InterruptedException {
        semaphore.acquire(); // 获取许可
        synchronized (connections) {
            return connections.poll();
        }
    }
    
    // 释放连接
    public void releaseConnection(Connection connection) {
        if (connection != null) {
            synchronized (connections) {
                connections.offer(connection);
            }
            semaphore.release(); // 释放许可
        }
    }
    
    // 尝试获取连接(非阻塞)
    public Connection tryGetConnection() {
        if (semaphore.tryAcquire()) {
            synchronized (connections) {
                return connections.poll();
            }
        }
        return null;
    }
    
    // 获取可用连接数
    public int getAvailableConnections() {
        return semaphore.availablePermits();
    }
    
    private Connection createConnection() {
        // 模拟创建数据库连接
        return new MockConnection();
    }
    
    static class MockConnection implements Connection {
        // 模拟连接实现
    }
}

6.2.3 并行任务协调

/**
 * 并行任务协调 - 使用CountDownLatch和CyclicBarrier
 */
public class ParallelTaskCoordinator {
    
    // 使用CountDownLatch等待所有任务完成
    public void executeTasksWithCountDownLatch() {
        int taskCount = 5;
        CountDownLatch latch = new CountDownLatch(taskCount);
        
        for (int i = 0; i < taskCount; i++) {
            final int taskId = i;
            new Thread(() -> {
                try {
                    System.out.println("Task " + taskId + " started");
                    Thread.sleep(1000 + taskId * 500); // 模拟任务执行
                    System.out.println("Task " + taskId + " completed");
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    latch.countDown();
                }
            }).start();
        }
        
        try {
            latch.await(); // 等待所有任务完成
            System.out.println("All tasks completed!");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    // 使用CyclicBarrier同步多个阶段
    public void executePhaseBasedTasks() {
        int workerCount = 3;
        CyclicBarrier barrier = new CyclicBarrier(workerCount, () -> {
            System.out.println("All workers reached barrier, proceeding to next phase");
        });
        
        for (int i = 0; i < workerCount; i++) {
            final int workerId = i;
            new Thread(() -> {
                try {
                    for (int phase = 1; phase <= 3; phase++) {
                        System.out.println("Worker " + workerId + " executing phase " + phase);
                        Thread.sleep(1000 + workerId * 200); // 模拟工作
                        System.out.println("Worker " + workerId + " finished phase " + phase);
                        
                        barrier.await(); // 等待其他工作线程
                    }
                } catch (InterruptedException | BrokenBarrierException e) {
                    Thread.currentThread().interrupt();
                }
            }).start();
        }
    }
}

七、性能对比分析

7.1 读写比例对性能的影响

/**
 * 不同读写比例下的性能测试
 */
public class ReadWriteRatioPerformanceTest {
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final ReentrantLock exclusiveLock = new ReentrantLock();
    private volatile int data = 0;
    
    // 使用读写锁的操作
    public int readWithRWLock() {
        rwLock.readLock().lock();
        try {
            Thread.sleep(1); // 模拟读操作
            return data;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return -1;
        } finally {
            rwLock.readLock().unlock();
        }
    }
    
    public void writeWithRWLock(int value) {
        rwLock.writeLock().lock();
        try {
            Thread.sleep(5); // 模拟写操作
            data = value;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            rwLock.writeLock().unlock();
        }
    }
    
    // 使用独占锁的操作
    public int readWithExclusiveLock() {
        exclusiveLock.lock();
        try {
            Thread.sleep(1);
            return data;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return -1;
        } finally {
            exclusiveLock.unlock();
        }
    }
    
    public void writeWithExclusiveLock(int value) {
        exclusiveLock.lock();
        try {
            Thread.sleep(5);
            data = value;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            exclusiveLock.unlock();
        }
    }
    
    // 性能测试方法
    public static void testPerformance(int readRatio, int writeRatio) {
        ReadWriteRatioPerformanceTest test = new ReadWriteRatioPerformanceTest();
        int totalOperations = 1000;
        int threadCount = 10;
        
        System.out.println("\nTesting with read:write ratio = " + readRatio + ":" + writeRatio);
        
        // 测试读写锁
        long startTime = System.currentTimeMillis();
        CountDownLatch rwLatch = new CountDownLatch(threadCount);
        
        for (int i = 0; i < threadCount; i++) {
            new Thread(() -> {
                Random random = new Random();
                for (int j = 0; j < totalOperations; j++) {
                    if (random.nextInt(readRatio + writeRatio) < readRatio) {
                        test.readWithRWLock();
                    } else {
                        test.writeWithRWLock(j);
                    }
                }
                rwLatch.countDown();
            }).start();
        }
        
        try {
            rwLatch.await();
            long rwTime = System.currentTimeMillis() - startTime;
            System.out.println("ReadWriteLock time: " + rwTime + "ms");
            
            // 测试独占锁
            startTime = System.currentTimeMillis();
            CountDownLatch exclusiveLatch = new CountDownLatch(threadCount);
            
            for (int i = 0; i < threadCount; i++) {
                new Thread(() -> {
                    Random random = new Random();
                    for (int j = 0; j < totalOperations; j++) {
                        if (random.nextInt(readRatio + writeRatio) < readRatio) {
                            test.readWithExclusiveLock();
                        } else {
                            test.writeWithExclusiveLock(j);
                        }
                    }
                    exclusiveLatch.countDown();
                }).start();
            }
            
            exclusiveLatch.await();
            long exclusiveTime = System.currentTimeMillis() - startTime;
            System.out.println("Exclusive lock time: " + exclusiveTime + "ms");
            System.out.println("Performance improvement: " + 
                             String.format("%.2f", (double)exclusiveTime / rwTime) + "x");
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    public static void main(String[] args) {
        // 测试不同的读写比例
        testPerformance(9, 1);  // 90% 读,10% 写
        testPerformance(7, 3);  // 70% 读,30% 写
        testPerformance(5, 5);  // 50% 读,50% 写
        testPerformance(3, 7);  // 30% 读,70% 写
        testPerformance(1, 9);  // 10% 读,90% 写
    }
}

7.2 锁竞争程度对性能的影响

/**
 * 锁竞争程度对性能的影响测试
 */
public class LockContentionPerformanceTest {
    
    public static void testLockContention() {
        int[] threadCounts = {1, 2, 4, 8, 16, 32};
        
        for (int threadCount : threadCounts) {
            System.out.println("\nTesting with " + threadCount + " threads:");
            
            // 测试独占锁
            testExclusiveLock(threadCount);
            
            // 测试共享锁(纯读操作)
            testSharedLock(threadCount);
        }
    }
    
    private static void testExclusiveLock(int threadCount) {
        ReentrantLock lock = new ReentrantLock();
        CountDownLatch latch = new CountDownLatch(threadCount);
        AtomicLong totalTime = new AtomicLong(0);
        
        for (int i = 0; i < threadCount; i++) {
            new Thread(() -> {
                long startTime = System.nanoTime();
                for (int j = 0; j < 10000; j++) {
                    lock.lock();
                    try {
                        // 模拟临界区操作
                        Thread.yield();
                    } finally {
                        lock.unlock();
                    }
                }
                totalTime.addAndGet(System.nanoTime() - startTime);
                latch.countDown();
            }).start();
        }
        
        try {
            latch.await();
            System.out.println("Exclusive lock average time per thread: " + 
                             totalTime.get() / threadCount / 1_000_000 + "ms");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    private static void testSharedLock(int threadCount) {
        ReadWriteLock rwLock = new ReentrantReadWriteLock();
        CountDownLatch latch = new CountDownLatch(threadCount);
        AtomicLong totalTime = new AtomicLong(0);
        
        for (int i = 0; i < threadCount; i++) {
            new Thread(() -> {
                long startTime = System.nanoTime();
                for (int j = 0; j < 10000; j++) {
                    rwLock.readLock().lock();
                    try {
                        // 模拟读操作
                        Thread.yield();
                    } finally {
                        rwLock.readLock().unlock();
                    }
                }
                totalTime.addAndGet(System.nanoTime() - startTime);
                latch.countDown();
            }).start();
        }
        
        try {
            latch.await();
            System.out.println("Shared lock average time per thread: " + 
                             totalTime.get() / threadCount / 1_000_000 + "ms");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

八、最佳实践

8.1 选择合适的锁类型

/**
 * 锁选择的最佳实践
 */
public class LockSelectionBestPractices {
    
    // 1. 读多写少场景:使用读写锁
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private Map<String, String> cache = new HashMap<>();
    
    public String getFromCache(String key) {
        rwLock.readLock().lock();
        try {
            return cache.get(key);
        } finally {
            rwLock.readLock().unlock();
        }
    }
    
    public void putToCache(String key, String value) {
        rwLock.writeLock().lock();
        try {
            cache.put(key, value);
        } finally {
            rwLock.writeLock().unlock();
        }
    }
    
    // 2. 需要超时或中断响应:使用ReentrantLock
    private final ReentrantLock timeoutLock = new ReentrantLock();
    
    public boolean tryOperationWithTimeout() {
        try {
            if (timeoutLock.tryLock(5, TimeUnit.SECONDS)) {
                try {
                    // 执行操作
                    return true;
                } finally {
                    timeoutLock.unlock();
                }
            } else {
                System.out.println("Failed to acquire lock within timeout");
                return false;
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
    }
    
    // 3. 简单的互斥访问:使用synchronized
    private int counter = 0;
    
    public synchronized void incrementCounter() {
        counter++;
    }
    
    // 4. 控制并发访问数量:使用Semaphore
    private final Semaphore semaphore = new Semaphore(3);
    
    public void accessLimitedResource() {
        try {
            semaphore.acquire();
            try {
                // 访问受限资源
                System.out.println("Accessing limited resource");
                Thread.sleep(1000);
            } finally {
                semaphore.release();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

8.2 避免常见陷阱

/**
 * 锁使用的常见陷阱和解决方案
 */
public class LockPitfallsAndSolutions {
    
    // 陷阱1:死锁
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();
    
    // 错误的做法:可能导致死锁
    public void badMethod1() {
        synchronized(lock1) {
            synchronized(lock2) {
                // 业务逻辑
            }
        }
    }
    
    public void badMethod2() {
        synchronized(lock2) {
            synchronized(lock1) {
                // 业务逻辑
            }
        }
    }
    
    // 正确的做法:按固定顺序获取锁
    public void goodMethod1() {
        synchronized(lock1) {
            synchronized(lock2) {
                // 业务逻辑
            }
        }
    }
    
    public void goodMethod2() {
        synchronized(lock1) {
            synchronized(lock2) {
                // 业务逻辑
            }
        }
    }
    
    // 陷阱2:忘记释放锁
    private final ReentrantLock lock = new ReentrantLock();
    
    // 错误的做法:可能忘记释放锁
    public void badUnlockMethod() {
        lock.lock();
        if (someCondition()) {
            return; // 忘记释放锁!
        }
        lock.unlock();
    }
    
    // 正确的做法:使用try-finally
    public void goodUnlockMethod() {
        lock.lock();
        try {
            if (someCondition()) {
                return;
            }
            // 其他业务逻辑
        } finally {
            lock.unlock();
        }
    }
    
    // 陷阱3:读写锁的锁升级
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private String data = "initial";
    
    // 错误的做法:尝试锁升级(不支持)
    public void badLockUpgrade() {
        rwLock.readLock().lock();
        try {
            if (needsUpdate(data)) {
                // 这里会死锁!ReentrantReadWriteLock不支持锁升级
                rwLock.writeLock().lock();
                try {
                    data = updateData(data);
                } finally {
                    rwLock.writeLock().unlock();
                }
            }
        } finally {
            rwLock.readLock().unlock();
        }
    }
    
    // 正确的做法:先释放读锁,再获取写锁
    public void goodLockUpgrade() {
        rwLock.readLock().lock();
        boolean needsUpdate;
        try {
            needsUpdate = needsUpdate(data);
        } finally {
            rwLock.readLock().unlock();
        }
        
        if (needsUpdate) {
            rwLock.writeLock().lock();
            try {
                // 重新检查条件(双重检查)
                if (needsUpdate(data)) {
                    data = updateData(data);
                }
            } finally {
                rwLock.writeLock().unlock();
            }
        }
    }
    
    private boolean someCondition() { return true; }
    private boolean needsUpdate(String data) { return true; }
    private String updateData(String data) { return data + "_updated"; }
}

8.3 性能优化建议

/**
 * 锁性能优化的最佳实践
 */
public class LockPerformanceOptimization {
    
    // 1. 减少锁的粒度
    private final Object[] locks = new Object[16];
    private final Map<String, String> data = new ConcurrentHashMap<>();
    
    {
        for (int i = 0; i < locks.length; i++) {
            locks[i] = new Object();
        }
    }
    
    public void finegrainedLocking(String key, String value) {
        int lockIndex = Math.abs(key.hashCode()) % locks.length;
        synchronized(locks[lockIndex]) {
            data.put(key, value);
        }
    }
    
    // 2. 减少锁的持有时间
    private final ReentrantLock lock = new ReentrantLock();
    private List<String> items = new ArrayList<>();
    
    // 错误的做法:在锁内进行耗时操作
    public void badLongHoldTime(String item) {
        lock.lock();
        try {
            items.add(item);
            // 耗时的网络调用
            callRemoteService(item);
        } finally {
            lock.unlock();
        }
    }
    
    // 正确的做法:缩短锁持有时间
    public void goodShortHoldTime(String item) {
        lock.lock();
        try {
            items.add(item);
        } finally {
            lock.unlock();
        }
        // 在锁外进行耗时操作
        callRemoteService(item);
    }
    
    // 3. 使用读写锁优化读操作
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final Map<String, Object> cache = new HashMap<>();
    
    public Object optimizedRead(String key) {
        rwLock.readLock().lock();
        try {
            return cache.get(key);
        } finally {
            rwLock.readLock().unlock();
        }
    }
    
    // 4. 使用无锁数据结构
    private final AtomicInteger atomicCounter = new AtomicInteger(0);
    private final ConcurrentHashMap<String, String> concurrentMap = new ConcurrentHashMap<>();
    
    public void lockFreeOperations() {
        // 原子操作,无需加锁
        atomicCounter.incrementAndGet();
        
        // 线程安全的Map操作
        concurrentMap.put("key", "value");
    }
    
    private void callRemoteService(String item) {
        // 模拟远程服务调用
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

九、面试常见问题

9.1 核心概念问题

Q1: 独占锁和共享锁的主要区别是什么?

答案:

维度 独占锁 共享锁
并发性 同时只允许一个线程持有 允许多个线程同时持有
互斥性 完全互斥,读写都排斥 读读不互斥,读写互斥
适用场景 写操作、数据修改 读操作、数据查询
性能特点 安全性高,并发度低 并发度高,读性能好
典型实现 synchronized、ReentrantLock ReadWriteLock、Semaphore

Q2: 什么时候应该使用读写锁?

答案: 读写锁适用于以下场景:

  1. 读多写少:读操作频率远高于写操作
  2. 读操作耗时:读操作本身比较耗时
  3. 数据一致性要求:需要保证读写的数据一致性
  4. 性能敏感:对读操作的性能有较高要求

不适用场景:

  • 写操作频繁的场景
  • 读操作很快完成的场景
  • 简单的计数器等场景

Q3: ReentrantReadWriteLock支持锁升级吗?

答案: ReentrantReadWriteLock 不支持锁升级(从读锁升级到写锁),但支持锁降级(从写锁降级到读锁)。

// 锁降级示例(支持)
writeLock.lock();
try {
    // 写操作
    readLock.lock(); // 获取读锁
} finally {
    writeLock.unlock(); // 释放写锁
}
try {
    // 读操作
} finally {
    readLock.unlock(); // 释放读锁
}

// 锁升级(不支持,会死锁)
readLock.lock();
try {
    writeLock.lock(); // 这里会死锁!
    // ...
} finally {
    readLock.unlock();
}

9.2 实现原理问题

Q4: AQS如何区分独占锁和共享锁?

答案: AQS通过以下方式区分:

  1. 节点类型标记:

    static final Node SHARED = new Node();    // 共享模式
    static final Node EXCLUSIVE = null;       // 独占模式
    
  2. 不同的模板方法:

    • 独占锁:tryAcquire()、tryRelease()
    • 共享锁:tryAcquireShared()、tryReleaseShared()
  3. 不同的唤醒机制:

    • 独占锁:只唤醒一个后继节点
    • 共享锁:可能唤醒多个等待节点

Q5: 为什么共享锁需要传播机制?

答案: 共享锁的传播机制确保:

  1. 资源充足时:一个线程获取共享锁成功后,如果还有剩余资源,应该继续唤醒后续等待的共享锁线程
  2. 避免饥饿:防止共享锁线程长时间等待
  3. 提高并发度:最大化利用共享资源

文章标签

JavaJUC锁并发
CountDownLatch 源码分析及面试题
上一篇

CountDownLatch 源码分析及面试题

2025-11-19

ReentrantLock 源码解析及面试题
下一篇

ReentrantLock 源码解析及面试题

2025-11-19

冬眠

冬眠

博主

专注于技术、阅读与思考。在这里记录学习、思考与生活。

116
文章
3
分类
关注我
系列:Java 锁

第 4 篇,共 6 篇

上一篇

ReentrantLock 源码解析及面试题

下一篇

CountDownLatch 源码分析及面试题

文章目录

目录

  • 一、概述
  • 总结
  • 二、独占锁详解
  • 三、共享锁详解
  • 四、核心区别对比
  • 五、实现原理
  • 六、典型应用场景
  • 七、性能对比分析
  • 八、最佳实践
  • 九、面试常见问题

相关文章

查看更多
JUC 中的锁

JUC 中的锁

2025-11-19 · 11 min read

AbstractQueuedSynchronizer (AQS) 详解

AbstractQueuedSynchronizer (AQS) 详解

2025-11-19 · 23 min read

ReentrantLock 源码解析及面试题

ReentrantLock 源码解析及面试题

2025-11-19 · 32 min read