如何选择锁
- 当只有少量竞争者,使用synchronized
- 竞争者不少但是线程增长的趋势是能预估的,使用ReetrantLock
- synchronized不会造成死锁,jvm会自动释放死锁。
一、可重入锁ReentrantLock
1.ReentrantLock
介绍
java
中有两类锁,一类是Synchronized
,而另一类就是J.U.C
中提供的锁。ReentrantLock
与Synchronized
都是可重入锁,本质上都是lock
与unlock
的操作。
2.ReentrantLock
和Synchronized
的对比
- 可重入性:两者的锁都是可重入的,差别不大,有线程进入锁,计数器自增
1
,等下降为0
时才可以释放锁; - 锁的实现:
synchronized
是基于JVM
实现的(用户很难见到,无法了解其实现),而ReentrantLock
是JDK实现的,可以通过查看源码来分析具体实现。 - 性能区别:在最初的时候,二者的性能差别差很多,当
synchronized
引入了偏向锁、轻量级锁(自选锁)后,二者的性能差别不大,官方推荐synchronized
(写法更容易、在优化时其实是借用了ReentrantLock
的CAS
技术,试图在用户态就把问题解决,避免进入内核态造成线程阻塞); - 功能区别:
1. 便利性:synchronized
更便利,它是由编译器保证加锁与释放。ReentrantLock
是需要手动释放锁,所以为了避免忘记手工释放锁造成死锁,所以最好在finally
中声明释放锁。
2. 锁的细粒度和灵活度:ReentrantLock
优于synchronized
;
3.ReentrantLock
的优势
ReentrantLock
可以指定是公平锁还是非公平锁,synchronized
只能是非公平锁。(所谓公平锁就是先等待的线程先获得锁)ReentrantLock
提供了一个Condition
类,可以分组唤醒需要唤醒的线程。不像是synchronized
要么随机唤醒一个线程,要么全部唤醒。- 提供能够中断等待锁的线程的机制,通过
lock.lockInterruptibly()
实现,这种机制ReentrantLock
是一种自选锁,通过循环调用CAS
操作来实现加锁。性能比较好的原因是避免了进入内核态的阻塞状态。
4.synchronized
的优势
从上边的介绍,看上去ReentrantLock
不仅拥有synchronized
的所有功能,而且有一些功能synchronized
无法实现的特性。性能方面,ReentrantLock
也不比synchronized
差,那么到底我们要不要放弃使用synchronized
呢?答案是不要这样做。
synchronized
的优势
J.U.C
包中的锁定类是用于高级情况和高级用户的工具,除非说你对Lock
的高级特性有特别清楚的了解以及有明确的需要,或这有明确的证据表明同步已经成为可伸缩性的瓶颈的时候,否则我们还是继续使用synchronized
。相比较这些高级的锁定类,synchronized
还是有一些优势的,比如synchronized
不可能忘记释放锁。还有当JVM
使用synchronized
管理锁定请求和释放时,JVM
在生成线程转储时能够包括锁定信息,这些信息对调试非常有价值,它们可以标识死锁以及其他异常行为的来源。
5.ReentrantLock
的使用示例
代码如下所示:
@Slf4j
@NotThreadSafe
public class LockExample2 {
// 请求总数
public static int clientTotal = 5000;
// 同时并发执行的线程数
public static int threadTotal = 200;
public static int count = 0;
//声明锁的实例
private final static Lock lock = new ReentrantLock();
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.