为什么synchronized、Lock的实现类/ReentrantLock都默认使用非公平锁???原来如此!!!

  • Post author:
  • Post category:其他


前言

在 Java 中

synchronized



ReentrantLock

是我们常用的加锁方式,在学习这两种加锁方式的原理时,公平锁和非公平锁的概念是必须要了解的,并且 synchronized 和 ReentrantLock 默认使用的都是非公平锁,**为什么都采用非公平锁呐?**原因:

都是为了提高程序的执行性能












哪公平锁和非公平锁性能差异在什么地方呐???











================

概念:

非公平锁:每个线程获取锁的顺序是随机的,并不会遵循先来先得的规则,任何线程在某时刻都有可能直接获取并拥有锁。注:非公平性是指排队等待的元素只有头节点参与竞争,进入排队的元素是按照顺序等待的;

​ 优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。

​ 缺点:你们可能也发现了,这样可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死。

公平锁:与非公平锁相反,每个线程按照有序顺序获取锁;

​ 优点:所有的线程都能得到资源,不会饿死在队列中。

​ 缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。

性能对比:

公平锁和非公平锁的性能测试结果如下,以下测试数据来自于《Java并发编程实战》:

在这里插入图片描述

从上述结果可以看出,使用非公平锁的吞吐率(单位时间内成功获取锁的平均速率)要比公平锁高很多。

理论分析:


公平锁:

获取锁时,先将线程自己添加到等待队列的队尾并休眠,当某线程用完锁之后,会去唤醒等待队列中队首的线程尝试去获取锁,锁的使用顺序也就是队列中的先后顺序,在整个过程中,线程会从运行状态切换到休眠状态,再从休眠状态恢复成运行状态,但线程每次休眠和恢复都需要从用户态转换成内核态,而这个状态的转换是比较慢的,所以公平锁的执行速度会比较慢。


非公平锁:

当线程获取锁时,会先通过 CAS 尝试获取锁,如果获取成功就直接拥有锁,如果获取锁失败才会进入等待队列,等待下次尝试获取锁。这样做的好处是,获取锁不用遵循先到先得的规则,从而避免了线程休眠和恢复的操作,这样就加速了程序的执行效率。

注:synchronized 不支持公平锁,Lock类支持公平锁

线程休眠和唤醒:

**synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。**独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。


Lock锁

的线程的等待/唤醒依赖于

Condition

,Condition等待通知的本质就是等待队列 和 同步队列的交互的过程,跟object的wait()/notify()机制一样;Condition是基于同步锁state实现的,而objec是基于monitor模式实现的。

**总结:**由上可知,在多线程竞争资源加锁时,synchronized和Lock使线程

休眠和唤醒

的实现原理是一样的,同时非公平锁对线程的休眠和唤醒次数远小于公平锁,所以非公平锁的性能高于公平锁。



版权声明:本文为weixin_37598243原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。