Java 并发编程深度解析:从 synchronized 到 Lock 的演进与实践
引言
Java 并发编程一直是后端开发中的核心技术领域,随着多核处理器的普及和高并发应用的需求增长,掌握并发编程的核心原理和最佳实践变得尤为重要。本文将深入解析 Java 并发编程中的关键技术点,从传统的 synchronized 关键字到现代的 Lock 接口,通过具体的代码实例和原理分析,帮助开发者理解并发编程的本质和演进过程。
synchronized 关键字:Java 并发的基石
基本原理与实现
synchronized 是 Java 提供的内置锁机制,基于对象监视器(Monitor)实现。每个 Java 对象都有一个内置的监视器锁,当线程进入 synchronized 代码块时,会自动获取该锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class SynchronizedExample { private int counter = 0; private final Object lock = new Object(); public synchronized void incrementMethod() { counter++; } public void incrementBlock() { synchronized (lock) { counter++; } } public static synchronized void staticMethod() { System.out.println("静态同步方法"); } }
|
synchronized 的底层实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public void synchronizedMethod() { synchronized (this) { counter++; } }
public class MonitorImplementation {
private volatile Thread owner; private volatile int recursions; private Queue<Thread> entryList; private Set<Thread> waitSet; }
|
synchronized 的优化演进
JDK 1.6 引入了锁优化技术,包括偏向锁、轻量级锁和重量级锁:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class LockOptimization { private int value = 0; public void demonstrateLockEscalation() { synchronized (this) { value++; } new Thread(() -> { synchronized (this) { value++; } }).start(); for (int i = 0; i < 10; i++) { new Thread(() -> { synchronized (this) { try { Thread.sleep(100); value++; } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }).start(); } } }
|
Lock 接口:更灵活的并发控制
ReentrantLock 的核心特性
ReentrantLock 提供了比 synchronized 更丰富的功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.Condition; import java.util.concurrent.TimeUnit;
public class ReentrantLockExample { private final ReentrantLock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private int counter = 0; private boolean ready = false; public void interruptibleLock() throws InterruptedException { lock.lockInterruptibly(); try { counter++; } finally { lock.unlock(); } } public boolean tryLockExample() { if (lock.tryLock()) { try { counter++; return true; } finally { lock.unlock(); } } return false; } public boolean timeoutLock() throws InterruptedException { if (lock.tryLock(5, TimeUnit.SECONDS)) { try { counter++; return true; } finally { lock.unlock(); } } return false; } public void producer() throws InterruptedException { lock.lock(); try { counter++; ready = true; condition.signalAll(); } finally { lock.unlock(); } } public void consumer() throws InterruptedException { lock.lock(); try { while (!ready) { condition.await(); } System.out.println("消费数据: " + counter); } finally { lock.unlock(); } } }
|
AQS(AbstractQueuedSynchronizer)原理解析
ReentrantLock 基于 AQS 实现,AQS 是 Java 并发包的核心基础类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| public abstract class AbstractQueuedSynchronizer { private volatile int state; private transient volatile Node head; private transient volatile Node tail; static final class Node { volatile Thread thread; volatile Node prev; volatile Node next; volatile int waitStatus; } protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); } protected boolean tryRelease(int arg) { throw new UnsupportedOperationException(); } }
public class ReentrantLock implements Lock { private final Sync sync; abstract static class Sync extends AbstractQueuedSynchronizer { final boolean nonfairTryAcquire(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) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } } }
|
读写锁:优化读多写少场景
ReentrantReadWriteLock 的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.HashMap; import java.util.Map;
public class ReadWriteLockExample { private final Map<String, String> cache = new HashMap<>(); private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock(); private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock(); public String get(String key) { readLock.lock(); try { System.out.println(Thread.currentThread().getName() + " 正在读取"); Thread.sleep(100); return cache.get(key); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null; } finally { readLock.unlock(); } } public void put(String key, String value) { writeLock.lock(); try { System.out.println(Thread.currentThread().getName() + " 正在写入"); Thread.sleep(200); cache.put(key, value); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { writeLock.unlock(); } } public void lockDowngrade(String key, String value) { writeLock.lock(); try { cache.put(key, value); readLock.lock(); } finally { writeLock.unlock(); } try { String result = cache.get(key); System.out.println("读取结果: " + result); } finally { readLock.unlock(); } } }
|
性能对比与选择指南
基准测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.CountDownLatch;
public class LockPerformanceTest { private static final int THREAD_COUNT = 10; private static final int ITERATIONS = 1000000; private int synchronizedCounter = 0; private int lockCounter = 0; private final Object syncLock = new Object(); private final ReentrantLock reentrantLock = new ReentrantLock(); public void testSynchronized() throws InterruptedException { CountDownLatch latch = new CountDownLatch(THREAD_COUNT); long startTime = System.currentTimeMillis(); for (int i = 0; i < THREAD_COUNT; i++) { new Thread(() -> { for (int j = 0; j < ITERATIONS; j++) { synchronized (syncLock) { synchronizedCounter++; } } latch.countDown(); }).start(); } latch.await(); long endTime = System.currentTimeMillis(); System.out.println("synchronized 耗时: " + (endTime - startTime) + "ms"); System.out.println("synchronized 结果: " + synchronizedCounter); } public void testReentrantLock() throws InterruptedException { CountDownLatch latch = new CountDownLatch(THREAD_COUNT); long startTime = System.currentTimeMillis(); for (int i = 0; i < THREAD_COUNT; i++) { new Thread(() -> { for (int j = 0; j < ITERATIONS; j++) { reentrantLock.lock(); try { lockCounter++; } finally { reentrantLock.unlock(); } } latch.countDown(); }).start(); } latch.await(); long endTime = System.currentTimeMillis(); System.out.println("ReentrantLock 耗时: " + (endTime - startTime) + "ms"); System.out.println("ReentrantLock 结果: " + lockCounter); } }
|
选择指南
场景 |
推荐方案 |
理由 |
简单的互斥访问 |
synchronized |
语法简洁,JVM优化充分 |
需要超时或中断 |
ReentrantLock |
提供更灵活的控制 |
读多写少场景 |
ReadWriteLock |
读操作并发性能更好 |
公平性要求 |
ReentrantLock(fair) |
支持公平锁模式 |
条件变量需求 |
ReentrantLock + Condition |
比 wait/notify 更灵活 |
最佳实践与注意事项
1. 锁的正确使用模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class LockBestPractices { private final ReentrantLock lock = new ReentrantLock(); public void correctLockUsage() { lock.lock(); try { } finally { lock.unlock(); } } public void incorrectLockUsage() { lock.lock(); lock.unlock(); } private static final Object lock1 = new Object(); private static final Object lock2 = new Object(); public void avoidDeadlock() { synchronized (lock1) { synchronized (lock2) { } } } }
|
2. 性能优化建议
- 减少锁的粒度:使用更细粒度的锁来减少竞争
- 避免锁嵌套:减少死锁风险和性能开销
- 合理选择锁类型:根据具体场景选择最适合的锁机制
- 使用无锁数据结构:在可能的情况下使用 ConcurrentHashMap 等无锁容器
总结
Java 并发编程从 synchronized 到 Lock 的演进体现了对性能和灵活性的不断追求。synchronized 作为 Java 的内置锁机制,经过多年优化已经具备了很好的性能表现,适合大多数简单的同步场景。而 Lock 接口及其实现类则提供了更丰富的功能和更细粒度的控制,适合复杂的并发场景。
理解这些并发机制的底层原理和适用场景,能够帮助我们在实际开发中做出正确的技术选择,编写出高性能、线程安全的并发程序。随着 Java 并发编程的不断发展,掌握这些核心概念将为我们应对更复杂的并发挑战奠定坚实的基础。
在实际应用中,我们应该根据具体的业务场景和性能要求,选择最合适的并发控制机制,并始终遵循并发编程的最佳实践,确保程序的正确性和高效性。