互为补充,谁在引领并发领域——ReentrantLock 与 synchronized 的共舞
2023-10-28 15:49:48
Java并发中的锁实现:ReentrantLock与synchronized大比拼
导读
在Java并发编程中,锁是协调多线程访问共享资源的关键工具。ReentrantLock和synchronized是两种常用的锁实现,它们有着不同的实现原理、性能表现和使用场景。本文将深入探究这两种锁的异同,帮助你选择最适合你项目的锁实现。
原理揭秘:CAS与Monitor
ReentrantLock和synchronized都是通过JVM底层的CAS(Compare and Swap)操作来实现锁的获取和释放。
ReentrantLock:可重入计数器
ReentrantLock是一种可重入锁,它通过维护一个计数器来实现锁的获取和释放。当一个线程获取锁时,计数器加1;当它释放锁时,计数器减1。当计数器为0时,表示锁处于未被获取的状态。
ReentrantLock还支持公平锁和非公平锁两种模式。公平锁保证了线程获取锁的顺序与它们请求锁的顺序是一致的;非公平锁则允许线程以任意顺序获取锁。
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
lock.lock();
try {
// 执行临界区代码
} finally {
lock.unlock();
}
});
Thread thread2 = new Thread(() -> {
lock.lock();
try {
// 执行临界区代码
} finally {
lock.unlock();
}
});
thread1.start();
thread2.start();
}
}
synchronized:JVM Monitor
synchronized是Java语言中内置的同步,它通过JVM的Monitor机制来实现锁的获取和释放。当一个线程执行synchronized代码块时,它必须先获取该代码块对应的锁对象。如果锁对象已经被其他线程持有,那么该线程将被挂起,直到锁对象被释放为止。
synchronized还支持重入,这意味着一个线程可以多次获取同一个锁对象。但是,它不支持公平锁,因此线程获取锁的顺序是不可预测的。
public class SynchronizedExample {
private static Object lock = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (lock) {
// 执行临界区代码
}
});
Thread thread2 = new Thread(() -> {
synchronized (lock) {
// 执行临界区代码
}
});
thread1.start();
thread2.start();
}
}
性能表现:ReentrantLock更胜一筹
在性能方面,ReentrantLock通常优于synchronized。这是因为ReentrantLock是基于CAS操作实现的,而synchronized则依赖于JVM的Monitor机制。Monitor机制需要在内核态和用户态之间进行切换,这会带来额外的性能开销。
在高并发场景下,ReentrantLock的性能优势尤为明显。
使用场景:细粒度vs粗粒度
ReentrantLock和synchronized都有各自适用的场景。
ReentrantLock:细粒度控制
ReentrantLock适合于需要对共享资源进行细粒度控制的场景,例如在多线程环境下更新数据结构。
synchronized:粗粒度控制
synchronized适合于需要对共享资源进行粗粒度控制的场景,例如在多线程环境下访问一个全局变量。
总结
ReentrantLock和synchronized是Java并发编程中常用的锁实现,它们各有千秋。ReentrantLock的性能通常优于synchronized,适合于需要对共享资源进行细粒度控制的场景。synchronized则适合于需要对共享资源进行粗粒度控制的场景。在实际开发中,你需要根据实际情况选择合适的锁实现,以保证程序的正确性和性能。
常见问题解答
-
什么是锁?
锁是一种同步机制,它允许线程在访问共享资源时协调它们的访问,防止数据不一致。
-
为什么需要锁?
在多线程环境下,如果多个线程同时访问共享资源,可能会导致数据不一致或死锁。使用锁可以确保一次只有一个线程访问共享资源,从而保证数据的一致性。
-
什么是可重入锁?
可重入锁允许一个线程多次获取同一个锁对象。这对于防止死锁至关重要。
-
什么是公平锁和非公平锁?
公平锁保证了线程获取锁的顺序与它们请求锁的顺序是一致的。非公平锁则允许线程以任意顺序获取锁。
-
如何选择合适的锁实现?
在选择锁实现时,需要考虑共享资源的粒度、并发性以及性能要求。对于需要对共享资源进行细粒度控制的高并发场景,ReentrantLock通常是更好的选择。对于需要对共享资源进行粗粒度控制的低并发场景,synchronized则更适合。