本文目录导读:

- synchronized关键字的正确使用
- ReentrantLock的灵活使用
- ReadWriteLock提升读性能
- 使用高级并发工具
- 避免死锁的策略
- 使用CAS操作避免阻塞
- 实际业务场景 - 订单库存扣减
- 关键建议:
我来分享几个Java锁机制的案例,并说明如何规避并发问题。
synchronized关键字的正确使用
❌ 错误案例:
public class Counter {
private int count = 0;
// 没有同步,线程不安全
public void increment() {
count++;
}
public int getCount() {
return count;
}
}
✅ 正确案例:
public class SafeCounter {
private int count = 0;
private final Object lock = new Object();
public void increment() {
synchronized (lock) { // 使用独立的锁对象
count++;
}
}
public int getCount() {
synchronized (lock) { // 读取也需要同步
return count;
}
}
}
ReentrantLock的灵活使用
案例:带超时的锁获取
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;
public class TimeoutLockExample {
private final ReentrantLock lock = new ReentrantLock();
public boolean tryProcessWithTimeout() {
boolean acquired = false;
try {
// 尝试获取锁,最多等待1秒
acquired = lock.tryLock(1, TimeUnit.SECONDS);
if (acquired) {
// 执行临界区代码
System.out.println(Thread.currentThread().getName() + " 获取锁成功");
Thread.sleep(500); // 模拟业务处理
return true;
} else {
System.out.println(Thread.currentThread().getName() + " 获取锁超时");
return false;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
} finally {
if (acquired) {
lock.unlock(); // 确保释放锁
}
}
}
}
ReadWriteLock提升读性能
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class CacheManager {
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Map<String, Object> cache = new HashMap<>();
// 读操作 - 可以多个线程同时读
public Object get(String key) {
rwLock.readLock().lock();
try {
return cache.get(key);
} finally {
rwLock.readLock().unlock();
}
}
// 写操作 - 必须独占
public void put(String key, Object value) {
rwLock.writeLock().lock();
try {
cache.put(key, value);
} finally {
rwLock.writeLock().unlock();
}
}
}
使用高级并发工具
CountDownLatch - 等待多个线程完成
import java.util.concurrent.CountDownLatch;
public class ParallelTaskExample {
public void executeParallelTasks() {
int taskCount = 5;
CountDownLatch latch = new CountDownLatch(taskCount);
for (int i = 0; i < taskCount; i++) {
new Thread(() -> {
try {
// 执行任务
System.out.println(Thread.currentThread().getName() + " 执行中");
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown(); // 任务完成,计数器减1
}
}).start();
}
try {
latch.await(); // 等待所有任务完成
System.out.println("所有任务完成");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
避免死锁的策略
❌ 可能死锁的案例:
public class DeadlockExample {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void method1() {
synchronized (lock1) {
System.out.println("获取lock1");
synchronized (lock2) {
System.out.println("获取lock2");
}
}
}
public void method2() {
synchronized (lock2) { // 锁顺序不一致!
System.out.println("获取lock2");
synchronized (lock1) {
System.out.println("获取lock1");
}
}
}
}
✅ 避免死锁的方案:
public class SafeLockExample {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
// 方案1:固定锁顺序
public void method1() {
synchronized (lock1) {
synchronized (lock2) {
// 业务逻辑
}
}
}
public void method2() {
synchronized (lock1) { // 与method1顺序一致
synchronized (lock2) {
// 业务逻辑
}
}
}
// 方案2:使用tryLock避免死锁
private final ReentrantLock lockA = new ReentrantLock();
private final ReentrantLock lockB = new ReentrantLock();
public boolean safeTransfer() {
boolean lockAAcquired = false;
boolean lockBAcquired = false;
try {
lockAAcquired = lockA.tryLock(100, TimeUnit.MILLISECONDS);
if (lockAAcquired) {
lockBAcquired = lockB.tryLock(100, TimeUnit.MILLISECONDS);
if (lockBAcquired) {
// 两个锁都获取成功
return true;
}
}
return false; // 获取失败,不持有任何锁
} catch (InterruptedException e) {
return false;
} finally {
if (lockBAcquired) {
lockB.unlock();
}
if (lockAAcquired) {
lockA.unlock();
}
}
}
}
使用CAS操作避免阻塞
import java.util.concurrent.atomic.AtomicInteger;
public class CASExample {
private final AtomicInteger count = new AtomicInteger(0);
public void increment() {
// 无锁的CAS操作
count.incrementAndGet(); // 等价于 count++
}
public int getCount() {
return count.get();
}
}
实际业务场景 - 订单库存扣减
public class OrderService {
private final ReentrantLock orderLock = new ReentrantLock();
private int stock = 100; // 库存
private final Object stockLock = new Object();
// 方案1:使用synchronized
public synchronized boolean createOrderSync(String userId, int quantity) {
if (stock >= quantity) {
stock -= quantity;
return true;
}
return false;
}
// 方案2:使用ReentrantLock + 尝试机制
public boolean createOrderWithLock(String userId, int quantity) {
if (orderLock.tryLock()) { // 非阻塞获取锁
try {
if (stock >= quantity) {
// 模拟数据库操作
stock -= quantity;
return true;
}
return false;
} finally {
orderLock.unlock();
}
}
return false; // 获取锁失败,直接返回
}
// 方案3:细粒度锁(只锁库存操作)
public boolean createOrderOptimized(String userId, int quantity) {
// 非库存操作可以并行
// ... 校验用户信息等操作
synchronized (stockLock) {
if (stock >= quantity) {
stock -= quantity;
return true;
}
return false;
}
}
}
关键建议:
- 锁粒度最小化:只锁必要的代码块
- 避免嵌套锁:尽量减少锁嵌套,或保持固定顺序
- 使用超时机制:tryLock比lock更安全
- 优先使用并发工具:如AtomicInteger、ConcurrentHashMap等
- 读写分离:读多写少场景使用ReadWriteLock
- 及时释放锁:finally块中释放锁
- 避免锁内执行耗时操作:锁内代码尽量精简
这些案例涵盖了Java并发编程中常见的锁使用场景和规避并发问题的方法,希望能帮助你写出更安全高效的并发代码。