Java并发控制(synchronized、ReentrantLock、CountDownLatch)
Posted on 2022-05-12 09:56 召冠 阅读(444) 评论(0) 编辑 收藏 举报参考资料:
https://blog.csdn.net/zcl_love_wx/article/details/93977947
https://zhuanlan.zhihu.com/p/420560153
Java中锁的实现方式有两种:synchronized关键字和并发包中的锁类。synchronized这个同步关键字以前性能不是太理想,在随着不停的优化后,它已经成了同步的首选。
锁涉及的几个重要概念:重入锁、自旋锁、锁偏向、锁粗化、公平锁与非公平锁、轻量级锁与重量级锁、读写锁与独占锁(乐观锁与悲观锁)
synchronized、ReentrantLock 都属于可重入锁。
synchronized、ReentrantLock 都是悲观锁。
synchronized,ReentrantLock 都是独占锁。
synchronized 是重量锁。
synchronized 是非公平锁,ReentrantLock可以通过构造函数指定该锁是公平的还是非公平的,默认是非公平的。
问题1: 可重入锁如果加了两把,但是只释放了一把会出现什么问题?
答:程序卡死,线程不能出来,也就是说我们申请了几把锁,就需要释放几把锁。
问题2: 如果只加了一把锁,释放两次会出现什么问题?
答:会报错,java.lang.IllegalMonitorStateException。
CountDownLatch 与 CyclicBarrier
CountDownLatch 它是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
CyclicBarrier 也是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。
类锁和对象锁(重要)
Class A {
// ==>对象锁:普通实例方法默认同步监视器就是this,
// 即调用该方法的对象
public synchronized methodA() {
}
public methodB() {
// ==>对象锁:this表示是对象锁
synchronized(this){
}
}
// ==>类锁:修饰静态方法
public static synchronized methodC() {
}
public methodD(){
// ==>类锁:A.class说明是类锁
synchronized(A.class){}
}
// 普通方法:任何情况下调用时,都不会发生竞争
public common(){
}
}
类锁的5种形式:
Class A {
// 普通字符串属性
private String val;
// 静态属性
private static Object staticObj;
// ==>类锁情况1:synchronized修饰静态方法
public static synchronized methodA() {
}
public methodB(){
// ==>类锁情况2:同步块里的对象是类
synchronized(A.class){}
}
public methodC(){
// ==>类锁情况3:同步块里的对象是字符串
synchronized("ABC"){}
}
public methodD(){
// ==>类锁情况4:同步块里的对象是静态属性
synchronized(staticObj){}
}
public methodE(){
// ==>类锁情况5:同步块里的对象是字符串属性
synchronized(val){}
}
}
锁粗化也提醒了我们平时写代码时,尽量不要在循环内使用锁:
// 粗化前
for(int i=0;i<10000;i++){
// 这会导致频繁同步代码,无谓的消耗系统资源
synchronized(monitor){
doSomething...
}
}
// 粗化后
synchronized(monitor){
for(int i=0;i<10000;i++){
doSomething...
}
}
锁偏向
偏向锁指的是,当第一个线程请求时,会判断锁的对象头里的ThreadId字段的值,如果为空,则让该线程持有偏向锁,并将ThreadId的值置为当前线程ID。当前线程再次进入时,如果线程ID与ThreadId的值相等,则该线程就不会再重复获取锁了。因为锁的请求与释放是要消耗系统资源的。
如果有其他线程也来请求该锁,则偏向锁就会撤销,然后升级为轻量级锁。如果锁的竞争十分激烈,则轻量级锁又会升级为重量级锁。