扫描下方二维码或者微信搜索公众号 菜鸟飞呀飞
,即可关注微信公众号,阅读更多 Spring源码分析
和 Java并发编程
文章。
1. 2.
Mr羽墨青衫
,文章链接: 深入剖析Java重入锁ReentrantLock的实现原理 )。 从图中可以发现,当持有锁的线程T1释放锁以后,会唤醒同步队列中的T2线程,只要同步队列中有线程在等待获取锁,那么其他刚进来想要获取锁的人,就不能插队,包括T1自己还想要获取锁,也需要去排队,这样就保证了让等待时间最长的线程获取到锁,即保证了公平性。
对于非公平锁,线程获取锁的示意图可以用如下示意图表示。(图片来源于公众号: Mr羽墨青衫
,文章链接: 深入剖析Java重入锁ReentrantLock的实现原理 )。
非公平锁一定不公平吗?
饥饿
的情况。在最坏的情况下,可能存在某个线程一直获取不到锁。不过相比性能而言, 饥饿
问题可以暂时忽略,这可能就是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 |
非公平锁的耗时远远小于公平锁的耗时
,这说明非公平锁在并发情况下, 性能更好,吞吐量更大
。当线程数越多时,差异越明显。