扫描下方二维码或者微信搜索公众号
菜鸟飞呀飞
,便可关注微信公众号,阅读更多Spring源码分析
和Java并发编程
文章。java
1.
非公平锁必定不公平吗?2.
公平锁与非公平锁的性能谁更好?Mr羽墨青衫
,文章连接:深刻剖析Java重入锁ReentrantLock的实现原理)。从图中能够发现,当持有锁的线程T1释放锁之后,会唤醒同步队列中的T2线程,只要同步队列中有线程在等待获取锁,那么其余刚进来想要获取锁的人,就不能插队,包括T1本身还想要获取锁,也须要去排队,这样就保证了让等待时间最长的线程获取到锁,即保证了公平性。编程
对于非公平锁,线程获取锁的示意图能够用以下示意图表示。(图片来源于公众号:Mr羽墨青衫
,文章连接:深刻剖析Java重入锁ReentrantLock的实现原理)。设计模式
非公平锁必定不公平吗?
答案是不必定,对于刚刚上面图中展现的那种状况,此时非公平锁是不公平的。可是存在一种特殊状况,能够参考以下示意图,如当T1线程释放锁之后,AQS同步队列外部没有线程来争抢锁,T1线程在释放锁之后,本身也不须要获取锁了,此时T1由于唤醒的是T2,如今是有T2一个线程来抢锁,因此此时能获取到锁的线程必定是T2,这种状况下,非公平锁又是公平的了,由于此时即便同步队列中有T三、T四、...、Tn线程,可是T2等待的时间最长,因此是T2获取到锁(AQS的同步队列遵循的原则是FIFO)。饥饿
的状况。在最坏的状况下,可能存在某个线程一直获取不到锁。不过相比性能而言,饥饿
问题能够暂时忽略,这可能就是ReentrantLock默认建立非公平锁的缘由之一了。public class Demo {
// 公平锁
private static Lock fairLock = new ReentrantLock(true);
// 非公平锁
private static Lock nonFairLock = new ReentrantLock(false);
// 计数器
private static int fairCount = 0;
// 计数器
private static int nonFairCount = 0;
public static void main(String[] args) throws InterruptedException {
System.out.println("公平锁耗时: " + testFairLock(10));
System.out.println("非公平锁耗时: " + testNonFairLock(10));
System.out.println("公平锁累加结果: " + fairCount);
System.out.println("非公平锁累加结果: " + nonFairCount);
}
public static long testFairLock(int threadNum) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(threadNum);
// 建立threadNum个线程,让其以公平锁的方式,对fairCount进行自增操做
List<Thread> fairList = new ArrayList<>();
for (int i = 0; i < threadNum; i++) {
fairList.add(new Thread(() -> {
for (int j = 0; j < 10000; j++) {
fairLock.lock();
fairCount++;
fairLock.unlock();
}
countDownLatch.countDown();
}));
}
long startTime = System.currentTimeMillis();
for (Thread thread : fairList) {
thread.start();
}
// 让全部线程执行完
countDownLatch.await();
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
public static long testNonFairLock(int threadNum) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(threadNum);
// 建立threadNum个线程,让其以非公平锁的方式,对nonFairCountCount进行自增操做
List<Thread> nonFairList = new ArrayList<>();
for (int i = 0; i < threadNum; i++) {
nonFairList.add(new Thread(() -> {
for (int j = 0; j < 10000; j++) {
nonFairLock.lock();
nonFairCount++;
nonFairLock.unlock();
}
countDownLatch.countDown();
}));
}
long startTime = System.currentTimeMillis();
for (Thread thread : nonFairList) {
thread.start();
}
// 让全部线程执行完
countDownLatch.await();
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
}
复制代码
threadNum
个线程,而后让这threadNum
个线程并发的对变量进行累加10000次的操做,分别用公平锁和非公平锁来保证线程安全,最后分别统计出公平锁和非公平锁的耗时结果。threadNum = 10
时,重复三次测试的结果以下。次数 | 公平锁 | 非公平锁 |
---|---|---|
1 | 618ms | 22ms |
2 | 544ms | 20ms |
3 | 569ms | 15ms |
threadNum = 20
时,重复三次测试的结果以下。次数 | 公平锁 | 非公平锁 |
---|---|---|
1 | 1208ms | 25ms |
2 | 1146ms | 26ms |
3 | 1215ms | 19ms |
threadNum = 30
时,重复三次测试的结果以下。次数 | 公平锁 | 非公平锁 |
---|---|---|
1 | 1595ms | 28ms |
2 | 1543ms | 31ms |
3 | 1601ms | 31ms |
非公平锁的耗时远远小于公平锁的耗时
,这说明非公平锁在并发状况下,性能更好,吞吐量更大
。当线程数越多时,差别越明显。