JAVA为简化开发者开发提供了很多并发的工具,包括各种同步器,有了JDK我们只要学会简单使用类API即可。但这并不意味着不需要探索其具体的实现机制,本文从JDK源码角度简单讲讲并发时线程竞争的公平性。

所谓公平性指所有线程对临界资源申请访问权限的成功率都一样,不会让某些线程拥有优先权。我们知道CLH Node FIFO等待队列是一个先进先出的队列,那么是否就可以说每条线程获取锁时就是公平的呢?关于公平性这里分拆成三个点分别阐述:

① 准备入队列的节点,此情况讨论的是线程加入等待队列时产生的竞争是否公平,线程在尝试获取锁失败后将被加入等待队列,这时多个线程通过自旋将节点加入队列,所有线程在自旋过程中是无法保证其公平性的,可能后来的线程比早到的先进入队列,所以节点入队列不具公平性。
        ② 等待队列中的节点,情况①中成功加入队列后即成为等待队列中的节点,我们知道此队列是一个先入先出队列,那么很简单能得到,队列中的所有节点是公平的,他们都按照顺序等待自己被前驱节点唤醒并获取锁,所以等待队列中的节点具有公平性。
        ③ 闯入的节点,这种情况是指一个新线程到达共享资源边界时不管等待队列中是否存在其他等待节点它都将优先尝试去获取锁,这种称为可闯入策略。可闯入特性破坏了公平性,JDK的AQS对外体现的公平性主要由此体现,下面将对闯入特性展开分析。
        AQS提供的基础获取锁算法是一种可闯入的算法,即如果有新线程到来先进行一次获取尝试,不成功的情况下才将当前线程加入等待队列。如图2-5-9-6所示,等待队列中节点线程按照顺序一个接一个尝试去获取共享资源的使用权,某时刻头结点线程准备尝试获取的同时另外一条线程闯入,此线程并非直接加入等待队列的尾部,而是先跟头结点线程竞争获取资源,闯入线程如果成功获取共享资源则直接执行,头结点线程则继续等待下一次尝试,如此一来闯入线程成功插队,后来的线程比早到的线程先执行,说明AQS基础获取算法是不严格公平的。

 
图2-5-9-6 闯入线程

基础获取算法逻辑简化如下:首先尝试获取锁,假如获取失败才创建节点并加入到等待队列的尾部,接着通过不断循环检查是否轮到自己执行,当然此过程为了提高性能可能将线程先挂起,最终由前驱节点唤醒。
if(尝试获取锁失败) {
    创建node
    使用CAS方式把node插入到队列尾部
    while(true){
    if(尝试获取锁成功 并且 node的前驱节点为头节点){
把当前节点设置为头节点
    跳出循环
}else{
    使用CAS方式修改node前驱节点的waitStatus标识为signal
    if(修改成功)
        挂起当前线程 
}
}
        为什么要使用闯入策略?可闯入的策略通常可以提供更高的总吞吐量。由于一般同步器颗粒度比较小,也可以说共享资源的范围较小,而线程从阻塞状态到被唤醒所消耗的时间周期可能是通过共享资源时间周期的几倍甚至几十倍,如此一来线程唤醒过程中将存在一个很大的时间周期空窗期,导致资源没有得到充分利用,为了提高吞吐量,引入这种闯入策略,它可以使在等待队列头结点从阻塞到被唤醒的时间段内闯入的线程直接获取锁并通过同步器,以便充分利用唤醒过程这一空窗期,大大增加了吞吐率。另外,闯入机制的实现对外提供一种竞争调节机制,即开发者可以在自定义同步器中定义闯入尝试获取的次数,假设次数为n则不断重复获取直到n次都获取不成功才把线程加入等待队列中,随着次数n的增加可以增大成功闯入的几率。同时,这种闯入策略可能导致等待队列中的线程饥饿,因为锁可能一直被闯入的线程获取,但由于一般持有同步器的时间很短暂而避免饥饿的发生,反之如果保护的代码体很长并且持有同步器的时间较长,这将大大增加等待队列无限等待的风险。

在实际情况中还是要根据用户需求制定策略,在一个公平性要求很高的场景,则可以把闯入策略去除掉以达到公平。在自定义同步器中可以通过AQS预留方法tryAcquire方法实现,只需判断当前线程是否为等待队列中头结点对应的线程,若不是则直接返回false,尝试获取失败。但前面这种公平性是相对Java语法语义层面上的公平性,在现实中JDK的实现会直接影响线程执行的顺序。

喜欢研究java的同学可以交个朋友,下面是本人的微信号:

从JDK源码角度看java并发的公平性的更多相关文章

  1. 从JDK源码角度看java并发的原子性如何保证

    JDK源码中,在研究AQS框架时,会发现很多地方都使用了CAS操作,在并发实现中CAS操作必须具备原子性,而且是硬件级别的原子性,java被隔离在硬件之上,明显力不从心,这时为了能直接操作操作系统层面 ...

  2. 从JDK源码角度看java并发线程的中断

    线程的定义给我们提供了并发执行多个任务的方式,大多数情况下我们会让每个任务都自行执行结束,这样能保证事务的一致性,但是有时我们希望在任务执行中取消任务,使线程停止.在java中要让线程安全.快速.可靠 ...

  3. 从JDK源码角度看Short

    概况 Java的Short类主要的作用就是对基本类型short进行封装,提供了一些处理short类型的方法,比如short到String类型的转换方法或String类型到short类型的转换方法,当然 ...

  4. 从JDK源码角度看Byte

    Java的Byte类主要的作用就是对基本类型byte进行封装,提供了一些处理byte类型的方法,比如byte到String类型的转换方法或String类型到byte类型的转换方法,当然也包含与其他类型 ...

  5. 从JDK源码角度看Object

    Java的Object是所有其他类的父类,从继承的层次来看它就是最顶层根,所以它也是唯一一个没有父类的类.它包含了对象常用的一些方法,比如getClass.hashCode.equals.clone. ...

  6. 从JDK源码角度看Boolean

    Java的Boolean类主要作用就是对基本类型boolean进行封装,提供了一些处理boolean类型的方法,比如String类型和boolean类型的转换. 主要实现源码如下: public fi ...

  7. 从JDK源码角度看并发竞争的超时

    JDK中的并发框架提供的另外一个优秀机制是锁获取超时的支持,当大量线程对某一锁竞争时可能导致某些线程在很长一段时间都获取不了锁,在某些场景下可能希望如果线程在一段时间内不能成功获取锁就取消对该锁的等待 ...

  8. 从JDK源码角度看并发锁的优化

    在CLH锁核心思想的影响下,JDK并发包以CLH锁作为基础而设计,其中主要是考虑到CLH锁更容易实现取消与超时功能.比起原来的CLH锁已经做了很大的改造,主要从两方面进行了改造:节点的结构与节点等待机 ...

  9. 从JDK源码角度看线程池原理

    "池"技术对我们来说是非常熟悉的一个概念,它的引入是为了在某些场景下提高系统某些关键节点性能,最典型的例子就是数据库连接池,JDBC是一种服务供应接口(SPI),具体的数据库连接实 ...

随机推荐

  1. Linux下常用设置文件和文件夹读写权限操作

    1.查看权限 ls -l xxx.xxx (xxx.xxx是文件名)   2.常见权限 -rw------- (600) 只有所有者才有读和写的权限 -rw-r--r-- (644) 只有所有者才有读 ...

  2. Button 使用Command 按钮置灰未更新

    当Button绑定了Command,按钮在窗口弹出.或者其它事件时,会自动置灰. 有时,异步执行时间较长时,界面一些绑定了命令的Button,State不会变化,会一直置灰. 直到再次转移Focus. ...

  3. Node.js ZLIB

    Zlib 稳定性: 3 - 文档 可以通过以下方式访问这个模块: var zlib = require('zlib'); 这个模块提供了对 Gzip/Gunzip, Deflate/Inflate, ...

  4. MongDB PHP7

    ---恢复内容开始--- PHP7 Mongdb 扩展安装 我们使用 pecl 命令来安装: $ /usr/local/php7/bin/pecl install mongodb 执行成功后,会输出以 ...

  5. mybatis映射器配置细则

    前面三篇博客我们已经多次涉及到映射器的使用了,增删查基本上都用过一遍了,但是之前我们只是介绍了基本用法,实际上mybatis中映射器可以配置的地方还是非常多,今天我们就先来看看映射器还有哪些需要配置的 ...

  6. Android重绘ListView高度

    Android重绘ListView高度 经常会有这样需求,需要ListView默认将所有的条目显示出来,这就需要外层使用ScrollView,ScrollView里面放置一个重绘高度的ListView ...

  7. EasyUI常用组件(基础)

    ---------------------------------------------------------------------------------------------------- ...

  8. 深度学习&机器学习资源汇总1

    本篇博客的目地,是对工作学习过程中所遇所见的一些有关深度学习.机器学习的优质资源,作分类汇总,方便自己查阅,也方便他人学习借用. 主要会涉及一些优质的理论书籍和论文.一些实惠好用的工具库和开源库.一些 ...

  9. Android开发艺术探索——第二章:IPC机制(上)

    Android开发艺术探索--第二章:IPC机制(上) 本章主要讲解Android的IPC机制,首先介绍Android中的多进程概念以及多进程开发模式中常见的注意事项,接着介绍Android中的序列化 ...

  10. android拍照获得图片及获得图片后剪切设置到ImageView

    ok,这次的项目需要用到设置头像功能,所以做了个总结,直接进入主题吧. 先说说怎么 使用android内置的相机拍照然后获取到这张照片吧 直接上代码: Intent intentFromCapture ...