当前位置: 首页 > 科技观察

年底了我裁完兄弟自己也离职了,复习了Java锁的底层准备面试...

时间:2023-03-22 17:26:45 科技观察

年底,我解雇了我的兄弟并离开了这份工作。复习了一下Java锁底层,准备面试。。。也间接说明了ReentrantLock的工作原理。本篇继续聊一个话题,java并发合约交付中公平锁和非公平锁的区别是什么?2.什么是非公平锁?先说说什么是非公平锁。现在让我们回头看看下面的图片。如上图所示,现在线程1加了锁,然后线程2尝试加锁,失败后进入等待队列,被阻塞。然后线程1释放锁,准备唤醒线程2再次尝试加锁。注意此时线程2还在等待队列中,还没有开始尝试重新加锁呢!然而,不幸的事情发生了。就在这个时候,程咬金半路爆发了,来了个线程3!线程3突然尝试对ReentrantLock发起锁操作,这时候会发生什么?这很容易!线程2还没有来得及重试锁。也就是说,没有时间去尝试重新执行CAS操作,将state的值从0变为1!线程3冲上去直接做了一个CAS操作,试图把state的值从0变成1,结果成功了!一旦CAS操作成功,线程3会将变量“lockthread”设置为自己。放一张图给大家看下整个过程:线程2正按规律的排队去拿锁,但是你的线程3不按规矩,线程1刚把锁释放了,就跑过来拿了锁不区分。锁定。这导致线程2在被唤醒后重新尝试加锁并执行CAS操作,结果无疑是失败了!原因很简单!因为锁CAS操作是试图将状态从0变为1,结果是此时状态已经是1,所以CAS操作肯定会失败!一旦锁失效,线程2会继续留在等待队列中不断等待,等待线程3释放锁后才唤醒自己,真是可怜!先来的线程2加不了锁!另外这里放一张图给大家感受下线程2无奈的过程:上面的锁策略就是所谓的非公平锁!如果使用默认构造函数创建ReentrantLock对象,默认的锁策略是不公平的。在非公平锁策略下,并不一定是先到队列的线程就一定会先获得锁的机会,而是各种线程会随意抢占。那么我们要实现公平锁策略怎么办呢?也很简单,在构造ReentrantLock对象的时候传入一个true即可:ReentrantLocklock=newReentrantLock(true)。这时候就意味着让他使用公平锁的策略,那么公平锁到底是什么意思呢?3.什么是公平锁?我们回到第一张图,就是线程1刚刚释放锁后,线程2还没有来得及重新加锁的状态。同样,这时候,假设有一个线程3来了,突然出来,想要加锁。如果是公平锁策略,那么这个时候线程3不会像呆头呆脑一样直接盲目加锁。他会先做出判断:嗯?AQS的等候队列中,有人在排队吗?如果有人在排队,说明我前面有兄弟要锁!如果AQS队列中真的有线程在排队,那我的线程3就不能像傻子一样直接抢占加锁了。因为现在我们有了一个公平的策略,我们要按照先到先得的顺序排队,谁先进队列谁就先出队列锁定!所以,此时线程3做出判断,发现队列里有人排队时,就会乖乖排到队列后面,不会贸然加锁!同样,我们用下图直观的给大家展示整个过程:在上面的等待队列中,线程3会根据公平原则直接进入队尾进行排队。那么,线程2不是被唤醒了吗?他将再次尝试锁定CAS。这个时候没有人抢他,他当然可以锁定成功。然后,线程2将状态值改为1,同时将“锁定线程”设置为自己。最后,线程2从等待队列中退出队列。整个过程见下图:这就是公平锁策略。所有前来加锁的线程都按照先来后到的顺序在等待队列中排队,不会盲目抢占加锁,非常公平。4.总结完成。通过图文分析,相信大家都明白了什么是公平锁,什么是非公平锁!但是,要知道java并发包中默认的很多锁的策略是不公平的,即后面的线程可能先被锁,先来的线程后锁。一般情况下,不公平的策略并没有什么大问题,但是大家应该对这种策略有所了解。在开发的时候需要考虑权衡是使用公平策略还是不公平策略。