作为jvm最常用的锁,synchronized 和ReentrantLock经常会被拿来比较…
(下文一律把synchronized 简写成sync,望周知)
相同点:都是可重入锁(可重入锁也叫递归锁,用于避免获取递归方法的锁时形成死锁。sync是隐式的可重入锁)
不同点:sync使用简单,不用手动lock,unlock;ReentrantLock需要手动加锁释放锁,但是灵活性会更好一些(因为可以设置公平非公平锁,可以设置允许中断等等)。一般情况下直接用sync锁即可,复杂的情况再用reentrantLock。性能方面:sync更笨重(但是jdk某个版本之后引入了锁升级概念,对sync进行了优化,性能OK了很多)
文章目录
参考文章
有赞coder-Java锁的那些事儿
尚硅谷的这个juc专题的视频还是挺香的
ReentrantLock
1. 可以设置公平锁模式和非公平锁模式
- 公平锁可以使多个线程持有锁的概率趋于公平。举个例子,假设a,b,c三个线程同时在争夺同一把锁,那么该锁被获取的情况会接近于 a,b,c,a,b,c,a,b,c,雨露均沾。默认模式是非公平锁(unfair),非公平锁有个好处是能最大化地利用CPU的时间片,避免上下文切换造成的损耗
2. ReentrantLock底层原理
这个问题又可以叫做如何实现ReentrantLock…
提到ReentrantLock,必须要先了解AQS同步器…
我的理解是:实现ReentrantLock,
- 要有个同步队列用来实现“公平”,就是线程排队(reentrantLock是通过AQS实现的)…这里有点串行化的思想
- 要实现阻塞(reentrantLock实现时用的是LockSupport的park方法)
- 要实现唤醒(reentrantLock实现时用的是LockSupport的unpark方法)
(公平锁)大概的过程是:
多个线程abcd一起竞争锁(通过CAS的方式竞争锁),假设a抢到了,就执行任务。
剩下的bcd会被加入同步队列,被加入队列的线程会被阻塞(park)。
直到上一个抢到锁的线程(a)释放锁之后,队列才会又移除一个线程(b),并唤醒它去执行任务。
ps: 这个队列是双向链表,FIFO的类型
其实吧,线程结点插入链表时也会并发的,源码在这有控制(对于没有立即加入的nodes,他们会一直自旋直到加入队列)
3. AQS
略,还没细看。这玩意是并发的基础知识
4. CAS
处理并发的一种方式,属于乐观锁
5. ReentrantLock使用注意事项
记得手动unlock,不然会造成其他线程一直阻塞试图获取锁,最终导致死锁
synchronized
1. 可以修饰方法和代码块,修饰代码块时,会对括号里的对象加锁
2. static sync 和 sync分别对应类锁和对象锁的概念
凡是需要竞争同一把类锁的方法都是竞态的;
凡是需要竞争同一个对象锁的方法都是竞态的;
类锁和对象锁不是竞态的(比如People类锁,和People的一个对象p生成的对象锁不是互斥的)
3. jdk对sync做了哪些优化
略,大概是锁升级。讲这个又要扯到对象头巴拉巴拉
4. sync底层原理
和monitor有关,但我不是很清楚。。
5.为啥wait等关键字要搭配sync一起使用
还和monitor有关,但我不是很清楚。。