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();
}
}
总结
核心知识点回顾:
-
锁策略选择:
- 乐观锁:读多写少场景
- 悲观锁:写多读少场景
- 读写锁:明显的读多写少
- 自旋锁:锁持有时间短
-
CAS应用场景:
- 原子类实现
- 无锁数据结构
- 自旋锁实现
- 注意ABA问题及解决方案
-
synchronized优化:
- 锁升级:无锁 → 偏向锁 → 轻量级锁 → 重量级锁
- 锁消除:消除不必要的锁
- 锁粗化:合并连续的锁操作
-
性能优化建议:
- 减少锁的持有时间
- 减小锁的粒度
- 使用读写锁替代互斥锁
- 考虑使用无锁编程
理解这些底层原理有助于编写更高效、更安全的并发程序,并能够在遇到性能问题时进行有效的分析和优化。