当前位置: 首页 > 后端技术 > Java

访谈突击46:公平锁和非公平锁有什么区别?

时间:2023-04-02 00:39:21 Java

从公平的角度来看,Java中的锁可以分为两类:公平锁和非公平锁。但是公平锁和非公平锁有什么区别呢?哪个更好?Java有哪些应用场景?接下来我们一起来看看吧。字面上的公平锁:每个线程获取锁的顺序按照线程访问锁的顺序,总是前面的线程先获取锁。非公平锁:每个线程获取锁的顺序是随机的,不遵循先到先得的规则。所有线程都会竞争获取锁。例如,公平锁就像开车经过收费站。所有的车都会排队等待通过。先来的车先通过,如下图:通过收费站的顺序也是分别先来先到。李斯和王舞,这种情况是公平锁。非公平锁相当于老司机强行封杀驱动。他不会遵守排队规则,到达后会试图强行阻挡。如果阻止成功,他将通过它。排在后面,这种情况就是非公平锁。应用场景在Java语言中,synchronized锁和ReentrantLock锁默认都是非公平锁。当然我们在创建ReentrantLock的时候可以手动指定为公平锁,但是synchronized只能是非公平锁。ReentrantLock默认是非公平锁,可以在其源码实现中验证,如下源码所示:当使用newReentrantLock(true)时,可以创建公平锁,如下源码所示:Fairandunfair锁代码演示接下来我们使用ReentrantLock来演示公平锁和非公平锁的区别。首先定义一个公平锁,开启3个线程,每个线程执行两次加锁和释放锁并打印线程名的操作,如下代码所示:importjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.ReentrantLock;publicclassReentrantLockFairTest{staticLocklock=newReentrantLock(true);publicstaticvoidmain(String[]args)throwsInterruptedException{for(inti=0;i<3;i++){newThread(()->{for(intj=0;j<2;j++){锁.lock();System.out.println("当前线程:"+Thread.currentThread().getName());lock.unlock();}}).start();}}}上面程序的执行结果如下图所示:接下来我们使用非公平锁来执行上面的代码具体实现如下:importjava.util.concurrent.locks.Lock;导入java.util.concurrent.locks.ReentrantLock;公共类ReentrantLockFairTest{静态锁lock=newReentrantLock();publicstaticvoidmain(String[]args)throwsInterruptedException{for(inti=0;i<3;i++){newThread(()->{for(intj=0;j<2;j++){锁.lock();System.out.println("当前线程:"+Thread.currentThread().getName());lock.unlock();}}).start();上面程序的执行结果如下图所示:从上面的结果可以看出,使用公平锁线程获取锁的顺序是:A->B->C->A->B->C,即Acquirelocksinorder而不是fairlocks。获取锁的顺序是A->A->B->B->C->C,原因是当所有线程都在竞争锁的时候,因为当前执行线程处于活动状态,其他线程处于等待状态状态(并且需要被唤醒),所以当前线程总是会先获取锁,所以最终获取锁的顺序是:A->A->B->B->C->C执行过程分析公平锁执行流程在获取锁的时候,先把线程自己加入到等待队列的尾部,然后休眠。当线程用完锁时,它会唤醒等待队列头部的线程尝试获取锁。锁的使用顺序就是队列中的顺序。整个过程中,线程会从运行态切换到睡眠态,再从睡眠态恢复到运行态,但线程每次睡眠和恢复都需要从用户态转换到内核状态。而且这个状态的转换比较慢,所以公平锁的执行速度也会比较慢。不公平的锁执行过程当一个线程在获取锁的时候,会先尝试通过CAS获取锁。如果获取成功,则直接拥有锁。如果获取锁失败,则进入等待队列,等待下一次尝试获取锁。这样做的好处是锁的获取不需要遵循先到先得的规则,从而避免了线程休眠和回收的操作,从而加快了程序的执行效率。公平锁和非公平锁的性能测试结果如下。以下测试数据来自《Java并发编程实战》:从以上结果可以看出,使用非公平锁的吞吐率(单位时间内平均成功获取锁的速率)高于公平锁。高多了。优缺点分析公平锁的优点是锁资源按顺序均匀分配,不会出现线程饥饿的情况。它的缺点是按顺序唤醒线程开销大,执行性能不高。非公平锁的优点是执行效率高。谁先获得锁,锁就属于他。不会有“资历排行”和顺序唤醒。但缺点是资源分配的随机性很强,可能会出现线程饥饿的情况。.综上所述,在Java语言中,锁的默认实现是非公平锁,因为非公平锁效率更高,可以通过ReentrantLock手动指定为公平锁。非公平锁侧重于性能,而公平锁侧重于锁资源的平均分配,所以我们需要选择合适的场景来应用两者。判断是非在自己,名誉在别人,得失在人数。公众号:Java面试真题分析面试合集:https://gitee.com/mydb/interview