并发编程核心技术深度解析

21 阅读10分钟

1. 常见锁策略详解

1.1 乐观锁 vs 悲观锁

悲观锁实现示例

java

public class PessimisticLockExample {
    private final Object lock = new Object();
    private int sharedData = 0;
    
    public void updateData() {
        synchronized(lock) {  // 悲观锁:总是先加锁再操作
            sharedData++;
            System.out.println("Updated to: " + sharedData);
        }
    }
    
    public int getData() {
        synchronized(lock) {
            return sharedData;
        }
    }
}

乐观锁实现示例(基于版本号)

java

public class OptimisticLockExample {
    private volatile int data = 0;
    private volatile int version = 0;
    
    public boolean updateData(int expectedValue, int newValue) {
        int currentVersion = version;
        
        // 模拟一些处理时间
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
        
        // CAS操作:检查版本号是否变化
        synchronized(this) {
            if (this.version == currentVersion && this.data == expectedValue) {
                this.data = newValue;
                this.version++;  // 更新版本号
                System.out.println("Update successful: " + newValue);
                return true;
            } else {
                System.out.println("Update failed: version conflict");
                return false;
            }
        }
    }
    
    // 重试机制
    public void updateWithRetry(int newValue) {
        while (true) {
            int currentData = data;
            if (updateData(currentData, newValue)) {
                break;
            }
            // 冲突发生,重试前稍微等待
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
}

1.2 重量级锁 vs 轻量级锁

内核态 vs 用户态性能对比

java

public class LockPerformanceTest {
    private static final int ITERATIONS = 1000000;
    
    // 重量级锁测试(使用synchronized,可能涉及内核态切换)
    public void testHeavyweightLock() throws InterruptedException {
        Object lock = new Object();
        long startTime = System.currentTimeMillis();
        
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < ITERATIONS / threads.length; j++) {
                    synchronized(lock) {
                        // 简单的计数器操作
                    }
                }
            });
            threads[i].start();
        }
        
        for (Thread thread : threads) {
            thread.join();
        }
        
        long endTime = System.currentTimeMillis();
        System.out.println("Heavyweight lock time: " + (endTime - startTime) + "ms");
    }
    
    // 轻量级锁测试(使用AtomicLong,纯用户态操作)
    public void testLightweightLock() throws InterruptedException {
        AtomicLong counter = new AtomicLong(0);
        long startTime = System.currentTimeMillis();
        
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < ITERATIONS / threads.length; j++) {
                    counter.incrementAndGet();
                }
            });
            threads[i].start();
        }
        
        for (Thread thread : threads) {
            thread.join();
        }
        
        long endTime = System.currentTimeMillis();
        System.out.println("Lightweight lock time: " + (endTime - startTime) + "ms");
    }
}

1.3 挂起等待锁 vs 自旋锁

自旋锁实现

java

public class SpinLock {
    private final AtomicBoolean locked = new AtomicBoolean(false);
    
    public void lock() {
        // 自旋:忙等待,不释放CPU
        while (!locked.compareAndSet(false, true)) {
            // 可选的优化:减少CPU占用
            Thread.yield();  // 让出CPU时间片
        }
    }
    
    public void unlock() {
        locked.set(false);
    }
}

// 使用示例
public class SpinLockExample {
    private final SpinLock spinLock = new SpinLock();
    private int counter = 0;
    
    public void increment() {
        spinLock.lock();
        try {
            counter++;
        } finally {
            spinLock.unlock();
        }
    }
}

挂起等待锁模拟

java

public class SuspendedWaitLock {
    private boolean isLocked = false;
    
    public synchronized void lock() throws InterruptedException {
        // 挂起等待:释放CPU,进入等待状态
        while (isLocked) {
            wait();  // 线程挂起,等待唤醒
        }
        isLocked = true;
    }
    
    public synchronized void unlock() {
        isLocked = false;
        notify();  // 唤醒一个等待线程
    }
}

1.4 读写锁(ReadWriteLock)

读写锁实现

java

public class ReadWriteLockExample {
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
    private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
    private Map<String, String> data = new HashMap<>();
    
    // 读操作:允许多个线程同时读
    public String get(String key) {
        readLock.lock();
        try {
            return data.get(key);
        } finally {
            readLock.unlock();
        }
    }
    
    // 写操作:只允许一个线程写
    public void put(String key, String value) {
        writeLock.lock();
        try {
            data.put(key, value);
        } finally {
            writeLock.unlock();
        }
    }
    
    // 读多写少的场景测试
    public void testReadWriteLock() throws InterruptedException {
        // 初始化数据
        put("key1", "value1");
        put("key2", "value2");
        
        // 创建多个读线程
        Thread[] readers = new Thread[5];
        for (int i = 0; i < readers.length; i++) {
            readers[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    get("key1");
                    get("key2");
                }
            });
        }
        
        // 创建少量写线程
        Thread[] writers = new Thread[2];
        for (int i = 0; i < writers.length; i++) {
            final int writerId = i;
            writers[i] = new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    put("key" + writerId, "value" + j);
                }
            });
        }
        
        // 启动所有线程
        for (Thread reader : readers) reader.start();
        for (Thread writer : writers) writer.start();
        
        // 等待完成
        for (Thread reader : readers) reader.join();
        for (Thread writer : writers) writer.join();
    }
}

1.5 可重入锁 vs 不可重入锁

可重入锁示例

java

public class ReentrantExample {
    private final ReentrantLock lock = new ReentrantLock();
    
    public void outerMethod() {
        lock.lock();
        try {
            System.out.println("Outer method: lock count = " + lock.getHoldCount());
            innerMethod();  // 可重入:同一个线程可以再次获取锁
        } finally {
            lock.unlock();
        }
    }
    
    public void innerMethod() {
        lock.lock();  // 可重入:不会死锁
        try {
            System.out.println("Inner method: lock count = " + lock.getHoldCount());
        } finally {
            lock.unlock();
        }
    }
}

不可重入锁模拟(会导致死锁)

java

public class NonReentrantLock {
    private boolean isLocked = false;
    private Thread lockingThread = null;
    
    public synchronized void lock() throws InterruptedException {
        // 不可重入:如果当前线程已经持有锁,会死锁
        while (isLocked && lockingThread != Thread.currentThread()) {
            wait();
        }
        isLocked = true;
        lockingThread = Thread.currentThread();
    }
    
    public synchronized void unlock() {
        if (Thread.currentThread() == lockingThread) {
            isLocked = false;
            lockingThread = null;
            notify();
        }
    }
}

// 使用不可重入锁会导致死锁
public class DeadlockExample {
    private final NonReentrantLock lock = new NonReentrantLock();
    
    public void problematicMethod() throws InterruptedException {
        lock.lock();
        try {
            System.out.println("First lock acquired");
            anotherMethod();  // 这里会死锁!
        } finally {
            lock.unlock();
        }
    }
    
    public void anotherMethod() throws InterruptedException {
        lock.lock();  // 死锁:同一个线程无法再次获取锁
        try {
            System.out.println("This will never print");
        } finally {
            lock.unlock();
        }
    }
}

1.6 公平锁 vs 非公平锁

公平锁测试

java

public class FairLockTest {
    private final ReentrantLock fairLock = new ReentrantLock(true);  // 公平锁
    private final ReentrantLock unfairLock = new ReentrantLock(false); // 非公平锁
    
    public void testFairLock() throws InterruptedException {
        System.out.println("=== Testing Fair Lock ===");
        testLock(fairLock, "FairLock");
    }
    
    public void testUnfairLock() throws InterruptedException {
        System.out.println("=== Testing Unfair Lock ===");
        testLock(unfairLock, "UnfairLock");
    }
    
    private void testLock(ReentrantLock lock, String lockName) throws InterruptedException {
        int threadCount = 5;
        CountDownLatch startLatch = new CountDownLatch(1);
        CountDownLatch endLatch = new CountDownLatch(threadCount);
        
        for (int i = 0; i < threadCount; i++) {
            final int threadId = i;
            new Thread(() -> {
                try {
                    startLatch.await();  // 等待同时开始
                    lock.lock();
                    try {
                        System.out.println(lockName + " acquired by Thread " + threadId);
                        Thread.sleep(100);  // 模拟工作
                    } finally {
                        lock.unlock();
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    endLatch.countDown();
                }
            }).start();
        }
        
        startLatch.countDown();  // 同时释放所有线程
        endLatch.await();  // 等待所有线程完成
    }
    
    public static void main(String[] args) throws InterruptedException {
        FairLockTest test = new FairLockTest();
        test.testFairLock();
        Thread.sleep(1000);
        test.testUnfairLock();
    }
}

2. CAS(Compare and Swap)深度解析

2.1 CAS 原理详解

java

public class CASDemo {
    /**
     * 模拟CAS操作(实际由CPU指令实现)
     * @param address 内存地址
     * @param expectedValue 期望值
     * @param newValue 新值
     * @return 是否成功
     */
    public static native boolean compareAndSwap(Object address, int expectedValue, int newValue);
    
    // Java中的CAS使用示例
    public void demonstrateCAS() {
        AtomicInteger atomicInt = new AtomicInteger(0);
        
        // 多个线程同时尝试CAS操作
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    // CAS操作:比较当前值是否为0,如果是则设置为1
                    boolean success = atomicInt.compareAndSet(0, 1);
                    if (success) {
                        System.out.println(Thread.currentThread().getName() + " CAS成功!");
                        // 重置为0,让其他线程有机会
                        atomicInt.set(0);
                    }
                }
            });
            threads[i].start();
        }
        
        // 等待所有线程完成
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

2.2 原子类实现原理

自定义原子整数类

java

public class MyAtomicInteger {
    private volatile int value;
    
    public MyAtomicInteger(int initialValue) {
        this.value = initialValue;
    }
    
    public int get() {
        return value;
    }
    
    public boolean compareAndSet(int expect, int update) {
        // 这里应该使用native方法,这里用synchronized模拟
        synchronized(this) {
            if (this.value == expect) {
                this.value = update;
                return true;
            }
            return false;
        }
    }
    
    public int incrementAndGet() {
        int current;
        int next;
        do {
            current = get();
            next = current + 1;
        } while (!compareAndSet(current, next));
        return next;
    }
    
    // 测试自定义原子类
    public static void main(String[] args) throws InterruptedException {
        MyAtomicInteger atomicInt = new MyAtomicInteger(0);
        
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                atomicInt.incrementAndGet();
            }
        });
        
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                atomicInt.incrementAndGet();
            }
        });
        
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        
        System.out.println("Final value: " + atomicInt.get());  // 应该是20000
    }
}

2.3 自旋锁的完整实现

java

public class CASSpinLock {
    private final AtomicReference<Thread> owner = new AtomicReference<>();
    private int spinCount = 0;
    
    public void lock() {
        Thread currentThread = Thread.currentThread();
        
        // 自旋获取锁
        while (!owner.compareAndSet(null, currentThread)) {
            // 自旋等待,可以添加一些优化
            spinCount++;
            if (spinCount % 1000 == 0) {
                Thread.yield();  // 每1000次自旋让出CPU
            }
        }
        spinCount = 0;  // 重置自旋计数
    }
    
    public void unlock() {
        Thread currentThread = Thread.currentThread();
        // 只有锁的持有者才能释放锁
        if (!owner.compareAndSet(currentThread, null)) {
            throw new IllegalMonitorStateException("Current thread does not hold the lock");
        }
    }
    
    public boolean tryLock() {
        Thread currentThread = Thread.currentThread();
        return owner.compareAndSet(null, currentThread);
    }
    
    public boolean isLocked() {
        return owner.get() != null;
    }
    
    public Thread getOwner() {
        return owner.get();
    }
}

// 测试自旋锁
public class SpinLockTest {
    private final CASSpinLock spinLock = new CASSpinLock();
    private int counter = 0;
    
    public void testSpinLock() throws InterruptedException {
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    spinLock.lock();
                    try {
                        counter++;
                    } finally {
                        spinLock.unlock();
                    }
                }
            });
            threads[i].start();
        }
        
        for (Thread thread : threads) {
            thread.join();
        }
        
        System.out.println("Final counter: " + counter);  // 应该是10000
    }
}

2.4 ABA问题及解决方案

ABA问题演示

java

public class ABAProblem {
    private static AtomicReference<String> atomicRef = new AtomicReference<>("A");
    
    public static void demonstrateABAProblem() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            // 线程1:A -> B -> A
            atomicRef.compareAndSet("A", "B");
            System.out.println("Thread1: A -> B");
            
            atomicRef.compareAndSet("B", "A");
            System.out.println("Thread1: B -> A");
        });
        
        Thread t2 = new Thread(() -> {
            // 线程2:检查到A,以为没有变化
            try {
                Thread.sleep(100);  // 确保线程1先执行
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            
            boolean success = atomicRef.compareAndSet("A", "C");
            System.out.println("Thread2: A -> C, success: " + success);
            // 实际上中间经历了A->B->A的变化!
        });
        
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

使用AtomicStampedReference解决ABA问题

java

public class ABASolution {
    private static AtomicStampedReference<String> atomicStampedRef = 
        new AtomicStampedReference<>("A", 0);
    
    public static void demonstrateSolution() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            int[] stampHolder = new int[1];
            String current = atomicStampedRef.get(stampHolder);
            int currentStamp = stampHolder[0];
            
            // A -> B,版本号+1
            atomicStampedRef.compareAndSet("A", "B", currentStamp, currentStamp + 1);
            System.out.println("Thread1: A -> B, stamp: " + (currentStamp + 1));
            
            // B -> A,版本号再+1
            current = atomicStampedRef.get(stampHolder);
            currentStamp = stampHolder[0];
            atomicStampedRef.compareAndSet("B", "A", currentStamp, currentStamp + 1);
            System.out.println("Thread1: B -> A, stamp: " + (currentStamp + 1));
        });
        
        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(50);  // 确保线程1先开始
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            
            int[] stampHolder = new int[1];
            String current = atomicStampedRef.get(stampHolder);
            int originalStamp = stampHolder[0];
            
            // 线程2休眠,让线程1完成A->B->A操作
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            
            // 再次获取当前值和版本号
            current = atomicStampedRef.get(stampHolder);
            int currentStamp = stampHolder[0];
            
            // 尝试更新,但版本号已经变化,会失败
            boolean success = atomicStampedRef.compareAndSet("A", "C", originalStamp, originalStamp + 1);
            System.out.println("Thread2: A -> C, expected stamp: " + originalStamp + 
                             ", current stamp: " + currentStamp + ", success: " + success);
        });
        
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

3. synchronized原理深度剖析

3.1 synchronized锁升级过程

java

public class SynchronizedUpgrade {
    private static final Object lock = new Object();
    private static int counter = 0;
    
    /**
     * 演示synchronized锁升级过程
     */
    public static void demonstrateLockUpgrade() throws InterruptedException {
        // 阶段1:无竞争,偏向锁
        System.out.println("=== Phase 1: No Competition (Biased Lock) ===");
        synchronized(lock) {
            counter++;
            System.out.println("First lock: counter = " + counter);
        }
        
        // 阶段2:轻微竞争,轻量级锁
        System.out.println("=== Phase 2: Mild Competition (Lightweight Lock) ===");
        Thread t1 = new Thread(() -> {
            synchronized(lock) {
                counter++;
                System.out.println("Thread1: counter = " + counter);
            }
        });
        
        Thread t2 = new Thread(() -> {
            synchronized(lock) {
                counter++;
                System.out.println("Thread2: counter = " + counter);
            }
        });
        
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        
        // 阶段3:激烈竞争,重量级锁
        System.out.println("=== Phase 3: Heavy Competition (Heavyweight Lock) ===");
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    synchronized(lock) {
                        counter++;
                    }
                }
            });
            threads[i].start();
        }
        
        for (Thread thread : threads) {
            thread.join();
        }
        
        System.out.println("Final counter: " + counter);
    }
}

3.2 锁消除优化示例

java

public class LockElimination {
    /**
     * 锁消除示例:局部字符串拼接不会存在线程安全问题
     * JVM会消除这个不必要的锁
     */
    public String concatenateStrings(String str1, String str2) {
        StringBuffer sb = new StringBuffer();  // StringBuffer是线程安全的
        sb.append(str1);
        sb.append(str2);
        return sb.toString();
        // JVM检测到sb是局部变量,不会逃逸出方法,因此会消除锁操作
    }
    
    /**
     * 对比版本:使用StringBuilder(非线程安全但更快)
     */
    public String concatenateStringsOptimized(String str1, String str2) {
        StringBuilder sb = new StringBuilder();  // 非线程安全,但更快
        sb.append(str1);
        sb.append(str2);
        return sb.toString();
    }
    
    public void performanceTest() {
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            concatenateStrings("Hello", "World");
        }
        long endTime = System.currentTimeMillis();
        System.out.println("StringBuffer time: " + (endTime - startTime) + "ms");
        
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            concatenateStringsOptimized("Hello", "World");
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuilder time: " + (endTime - startTime) + "ms");
    }
}

3.3 锁粗化优化示例

java

public class LockCoarsening {
    private final Object lock = new Object();
    private int count = 0;
    
    /**
     * 细粒度锁:多次加锁解锁(性能差)
     */
    public void fineGrainedLocking() {
        // 不推荐的写法:频繁加锁解锁
        synchronized(lock) { count++; }
        synchronized(lock) { count++; }
        synchronized(lock) { count++; }
        synchronized(lock) { count++; }
        synchronized(lock) { count++; }
    }
    
    /**
     * 粗粒度锁:一次加锁包含所有操作(性能好)
     */
    public void coarseGrainedLocking() {
        // 推荐的写法:一次加锁包含所有相关操作
        synchronized(lock) {
            count++;
            count++;
            count++;
            count++;
            count++;
        }
    }
    
    /**
     * JVM自动锁粗化示例
     * 在循环中加锁,JVM可能会将锁粗化到循环外部
     */
    public void loopLockCoarsening() {
        // JVM可能会优化为在循环外部加锁
        for (int i = 0; i < 1000; i++) {
            synchronized(lock) {
                count++;
            }
        }
        // 优化后可能相当于:
        // synchronized(lock) {
        //     for (int i = 0; i < 1000; i++) {
        //         count++;
        //     }
        // }
    }
    
    /**
     * 性能对比测试
     */
    public void performanceComparison() throws InterruptedException {
        int iterations = 100000;
        
        // 测试细粒度锁性能
        long startTime = System.currentTimeMillis();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < iterations; i++) {
                fineGrainedLocking();
            }
        });
        
        // 测试粗粒度锁性能
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < iterations; i++) {
                coarseGrainedLocking();
            }
        });
        
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        
        long endTime = System.currentTimeMillis();
        System.out.println("Performance test completed in " + (endTime - startTime) + "ms");
    }
}

3.4 synchronized综合性能测试

java

public class SynchronizedPerformanceTest {
    private static final int THREAD_COUNT = 10;
    private static final int ITERATIONS = 100000;
    
    public static void testSynchronizedMethod() throws InterruptedException {
        Object lock = new Object();
        AtomicInteger counter = new AtomicInteger(0);
        
        Thread[] threads = new Thread[THREAD_COUNT];
        long startTime = System.currentTimeMillis();
        
        for (int i = 0; i < THREAD_COUNT; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < ITERATIONS / THREAD_COUNT; j++) {
                    synchronized(lock) {
                        counter.incrementAndGet();
                    }
                }
            });
            threads[i].start();
        }
        
        for (Thread thread : threads) {
            thread.join();
        }
        
        long endTime = System.currentTimeMillis();
        System.out.println("Synchronized method time: " + (endTime - startTime) + "ms");
        System.out.println("Final count: " + counter.get());
    }
    
    public static void testReentrantLock() throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        AtomicInteger counter = new AtomicInteger(0);
        
        Thread[] threads = new Thread[THREAD_COUNT];
        long startTime = System.currentTimeMillis();
        
        for (int i = 0; i < THREAD_COUNT; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < ITERATIONS / THREAD_COUNT; j++) {
                    lock.lock();
                    try {
                        counter.incrementAndGet();
                    } finally {
                        lock.unlock();
                    }
                }
            });
            threads[i].start();
        }
        
        for (Thread thread : threads) {
            thread.join();
        }
        
        long endTime = System.currentTimeMillis();
        System.out.println("ReentrantLock time: " + (endTime - startTime) + "ms");
        System.out.println("Final count: " + counter.get());
    }
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== Synchronized vs ReentrantLock Performance Test ===");
        testSynchronizedMethod();
        testReentrantLock();
    }
}

总结

核心知识点回顾:

  1. 锁策略选择

    • 乐观锁:读多写少场景
    • 悲观锁:写多读少场景
    • 读写锁:明显的读多写少
    • 自旋锁:锁持有时间短
  2. CAS应用场景

    • 原子类实现
    • 无锁数据结构
    • 自旋锁实现
    • 注意ABA问题及解决方案
  3. synchronized优化

    • 锁升级:无锁 → 偏向锁 → 轻量级锁 → 重量级锁
    • 锁消除:消除不必要的锁
    • 锁粗化:合并连续的锁操作
  4. 性能优化建议

    • 减少锁的持有时间
    • 减小锁的粒度
    • 使用读写锁替代互斥锁
    • 考虑使用无锁编程

理解这些底层原理有助于编写更高效、更安全的并发程序,并能够在遇到性能问题时进行有效的分析和优化。