标题:解锁并发新姿势:深入浅出Java的synchronized与ReentrantLock
在多线程编程的世界里,共享资源的访问如同一场没有硝烟的战争。如果缺乏妥善的同步机制,数据竞争、状态不一致等问题便会接踵而至。在Java中,我们有两把强大的“锁”来守护线程安全:老牌的 synchronized 关键字和后起之秀 ReentrantLock。
synchronized:简洁的守护者
synchronized是Java元老级的同步工具,以其语法简洁而闻名。你可以用它来修饰方法或代码块。
public class SynchronizedCounter {
private int count = 0;
public synchronized void increment() {
count++; // 这里是线程安全的
}
}
它的核心优势在于“开箱即用”。JVM负责其底层的加锁与解锁,并且它具备可重入性(一个线程可以多次获取同一把锁)和内置的锁释放机制(在同步块结束时自动释放)。
ReentrantLock:灵活的武士
ReentrantLock在Java 5时被引入,作为synchronized的一个更灵活、功能更丰富的替代品。
public class LockCounter {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock(); // 手动获取锁
try {
count++;
} finally {
lock.unlock(); // 必须在finally块中确保释放
}
}
}
它的灵活性体现在哪里?
- 尝试锁(Try Lock):你可以使用
tryLock()方法尝试获取锁,如果锁不可用,线程不会一直被阻塞,可以立即返回或执行其他任务。 - 公平锁:在构造函数中传入
true,可以创建一个公平锁,按照线程等待的先后顺序获取锁,减少“线程饥饿”现象。 - 可中断的锁等待:使用
lockInterruptibly()方法,在等待锁的过程中可以响应中断。
如何选择?
- 追求简洁与维护性:优先选择
synchronized。它的代码更清晰,不易出错(不会忘记释放锁)。 - 需要高级功能:当你的业务场景需要尝试锁、公平锁或可中断的锁等待时,
ReentrantLock是不二之选。
总结
synchronized和ReentrantLock都是Java并发工具箱中的利器。理解它们各自的原理和适用场景,能帮助我们在面对复杂的多线程问题时,做出最合适的技术选型,写出既安全又高效的程序。