Java并发编程锁之独占公平锁与非公平锁比较
在上一篇文章中,我们知道了非公平锁。其实Java中还存在着公平锁呢。公平二字怎么理解呢?和我们现实理解是一样的。大家去排队本着先来先得到的原则,在排队中,无论身份贵贱,一律平等对待。这是就是我们现实生活中的公平。大家都喜欢公平的。但是在Java中默认是非公平的,为什么呢?
本文主要内容:公平锁的现实生活理解;公平锁演示;为什么Java中默认是非公平锁(公平锁的非公平锁的比较)
本篇是《凯哥(凯哥Java:kagejava)并发编程学习》系列之《Lock系列》教程的第四篇:《Java并发包下锁学习第五篇:公平锁理解及与非公平锁的比较》。
同样还是去ATM机取钱的例子。假设现在有3个人使用ATM取钱。路人甲不会用ATM,自己摸索耗时5min,然后终于学会怎么使用了,但是密码又忘掉了。打电话给家里人咨询耗时1min.当路人甲操作完成之后,后面两个人排队接着依次操作,这种方式是谁先到谁先操作,操作完成之后下一个人才可以操作的,不管贫富贵贱,不管你是取100还是取1W,取1W的人在取100的人后面,就要排着队等着,这种看上去很公平的,无论贵贱,大家依次操作,这种操作模式站在多线程并发角度来看的话,就是公平锁操作。
在路人甲总耗时6min之后,路人乙和路人丁两个人操作耗时3min。也就是三个人总耗时9min.为什么会产生这种情况呢?因为路人甲堵着了。一直占着锁的资源。在路人甲操作的过程中,其他人只能排队等待。如果路人甲不会操作,排在他后面的路人丙插队询问路人甲,自己可以先插队操作ATM,同时教会路人甲。如果甲同意,则丙可以操作完成后,甲可以学着别人操作,有可能路人甲2min也能操作完成。这个时候,三个人总耗时就是5min了;如果甲不同意丙插队操作,那么丙只能回到原来位置上,进行排队等待。这种操作模式站在多线程并发角度来考虑的话,路人甲在模式及和家人通话耗时看着是CPU切换上下文的耗时。路人丙插队后获取ATM资源,这个操作可以看着是非公平的,因为丙的进入时间比路人乙晚,但是丙先操作了。但是从最后三人总耗时来看,路人丙插队,是的效率提高了。这种操作在Java并发中,称之为非公平锁。
需要说明的是,无论是显式锁还是隐式锁默认都是非公平的。因为非公平能够提升系统的吞吐量。
非公平锁的定义:
线程先尝试着获取同步状态操作(丙先尝试着插队),如果获取到,就对共享变量操作(甲同意丙的插队,丙就操作ATM机),如果获取不到,就接着排队。
需求:控制台打印的结果和线程顺序一致。代码如下:
此时代码和上一次代码唯一区别如上图:在实例化lock的是有个参数,设置了true.
运行结果:
从运行结果中,我们发现控制台打印出的获取锁的顺序和调用锁的时候顺序是一样的, 已经达到我们预期结果了。但是,还是每次只有一个线程操作,等这个线程操作完成释放锁后,其他线程才可以接着获取锁。
问题:
为什么并发大师Doug Lea把ReentrantLock设计默认模式是非公平的?
其实要回答这个问题,就需要从公平锁与非公平锁的不同来进行比较了。我们先来看看ATM机操作在公平锁和非公平锁的场景下,如下图:
公平锁:大家都排队,如果一个线程堵着了(路人甲),其他线程只能等待这个。最终,三个线程操作完成,总耗时9min.
非公平锁情况下:多个线程操作的共享资源的时候,发现共享资源还没有被锁定(路人甲还在摸索过程),就尝试插队(路人丙尝试和甲沟通,先插队操作并教会甲),如果插队成功(甲同意了),就操作共享资源(丙先操作ATM机);如果插队失败(甲不同意),接着排队(丙回到队伍中排队)。如果插队成功,最终耗时:5min.
从中我们可以看出公平锁和非公平锁的优缺点了。
优点:效率高;缺点:容易导致线程“饥饿”。当多个线程使用非公平的话,有可能有一个线程一直就获取不到竞争权,导致这个线程会“饥饿而死”。
适用场景:
如果在不考虑TPS(单位时间内成功完成的次数)作为唯一考量指标的场景下,可以使用非公平锁来操作,因为非公平锁能提高系统的吞吐量;
优点:避免了线程的“饥饿”;缺点:性能相对于公平锁会差很多。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。