从源码学习Java并发的锁是怎么维护内部线程队列的

在上一篇文章中,凯哥对同步组件基础框架- AbstractQueuedSynchronizer(AQS)做了大概的介绍。我们知道AQS能够通过内置的FIFO队列来完成资源获取线程的排队工作。那么AQS是怎么来维护这个排队工作的呢?今天我们就来扒一扒AQS源码。从源码中来看看是怎么维护对了的。

本篇是《凯哥(凯哥Java:kagejava)并发编程学习》系列之《Lock系列》教程的第一篇:《Java并发包下锁学习第三篇-从源码学习Java并发是怎么维护内部线程队列的》。

在上篇我们知道AQS内部有个内部类-Node对象。这个对象就是来维护线程对资源访问的排队工作的。具体怎么操作的呢?本文主要内容:Node节点介绍;在同步器中怎么为维护排队的流程图。

一:Node节点对象介绍

在AQS内部有个Node对象的内部类。我们来看看这个对象都有哪些属性:

简化后:

static final class Node {
//线程等待状态
volatile int waitStatus;
//当前节点的上一个节点
volatile Node prev;
//当前节点对象
volatile Node next;
//当前节点维护的线程对象
volatile Thread thread;
//当前节点的下一个(后续)节点
Node nextWaiter;
}

对象中属性介绍

Int waitStatus:

对象里面有表示状态的4个属性:

static final int CANCELLED = 1:线程从同步队列中取消

static final int SIGNAL = -1:后续节点等待状态。当前节点在获取到资源后,在释放前需要断开和后续节点的连接。在其释放后,会通知后续节点,使后续解决继续运行。

static final int CONDITION = -2:当前节点等待中。在等待condition通知。也可以理解成在condition队列中。

static final int PROPAGATE = -3:在共享模式下,下一次无条件传播

0:默认状态。

Node prev:当前节点的上一个节点

Node Next:当前解决的后续节点

Node nextWaiter:可以理解为节点的类型。是共享式还是独占式。

Thread thread:当前获取到同步状态的线程对象。

具体可以如下图:

首先,我们需要明白,在数据结构中,能够保持FIFO的结构是队列模式的。但是队列有单项队列和循环队列两种。那么,同步器使用的是哪个队列方式呢?

从Node节点属性中,我们可以看到前节点和后续节点的属性。说明使用的是循环队列。

二:维护线程排队的流程图

为了保证线程的安全,同步器提供了几个CAS的方法。如下图:

CAS设置头节点、设置下一个节点、设置状态、设置尾节点等。

操作流程可以简述如下图:

流程说明:

入队列

入队流程如下:

上图流程说明:

当多个线程同时来争夺资源的时候,其中一个线程获取到了资源(同步状态或者是锁),这个时候获取到资源的线程就会被构造成头节点。其他线无非获取到资源的线程会被构成成Node节点对象并被放到队列中。被构造成Node节点的线程会排在队列尾部排队。为了保证线程安全性,同步器会基于CAS设置尾节点的方法(即:compareAndSetTail ())来保持线程安全性.这个方法需要传递当前线程“自己认为”的尾节点和前一个节点,当CAS执行成功之后,当前节点才会正式与之前的节点建立关系。被设置尾部的Node节点的next将指向头节点。

如上图中线程3会和线程1执行类似的操作,把自己添加到队列的尾部。这样就形成了一个完整的双向队列排队了。

出队列

出队流程图如下:

出队流程说明:

从入队流程图中我们可以看出,所有争夺资源并发的线程都被排队了。同步队列遵循FIFO(先进先出)。所谓的首节点就是获取同步状态成功节点。当来的首节点中的线程在释放同步状态的时候,会断开自己与后续节点的关联关系,然后会唤醒后续节点操作的。当后续节点获取同步状态成功的时候,就将自己设置为首节点,原来的首节点就退出了队列。如果原来的首节点还需要获取的话,后将自己线程构造成Node节点对象,然后进行排队。

从源码学习Java并发的锁是怎么维护内部线程队列的的更多相关文章

  1. Java源码学习 -- java.lang.StringBuilder,java.lang.StringBuffer,java.lang.AbstractStringBuilder

    一直以来,都是看到网上说“ StringBuilder是线程不安全的,但运行效率高:StringBuffer 是线程安全的,但运行效率低”,然后默默记住:一个是线程安全.一个线程不安全,但对内在原因并 ...

  2. Java源码学习 -- java.lang.String

    java.lang.String是使用频率非常高的类.要想更好的使用java.lang.String类,了解其源代码实现是非常有必要的.由java.lang.String,自然联想到java.lang ...

  3. 【Java并发编程】面试常考的ThreadLocal,超详细源码学习

    目录 ThreadLocal是啥?用来干啥? ThreadLocal的简单使用 ThreadLocal的实现思路? ThreadLocal常见方法源码分析 ThreadLocal.set(T valu ...

  4. Java并发包源码学习系列:ReentrantLock可重入独占锁详解

    目录 基本用法介绍 继承体系 构造方法 state状态表示 获取锁 void lock()方法 NonfairSync FairSync 公平与非公平策略的差异 void lockInterrupti ...

  5. Java并发包源码学习系列:基于CAS非阻塞并发队列ConcurrentLinkedQueue源码解析

    目录 非阻塞并发队列ConcurrentLinkedQueue概述 结构组成 基本不变式 head的不变式与可变式 tail的不变式与可变式 offer操作 源码解析 图解offer操作 JDK1.6 ...

  6. Java源码之 java.util.concurrent 学习笔记01

    准备花点时间看看 java.util.concurrent这个包的源代码,来提高自己对Java的认识,努力~~~ 参阅了@梧留柒的博客!边看源码,边通过前辈的博客学习! 包下的代码结构分类: 1.ja ...

  7. Java并发包源码学习系列:CLH同步队列及同步资源获取与释放

    目录 本篇学习目标 CLH队列的结构 资源获取 入队Node addWaiter(Node mode) 不断尝试Node enq(final Node node) boolean acquireQue ...

  8. Java并发包源码学习系列:AQS共享式与独占式获取与释放资源的区别

    目录 Java并发包源码学习系列:AQS共享模式获取与释放资源 独占式获取资源 void acquire(int arg) boolean acquireQueued(Node, int) 独占式释放 ...

  9. Java并发包源码学习系列:ReentrantReadWriteLock读写锁解析

    目录 ReadWriteLock读写锁概述 读写锁案例 ReentrantReadWriteLock架构总览 Sync重要字段及内部类表示 写锁的获取 void lock() boolean writ ...

随机推荐

  1. LISTAGG函数

    官网进入 该函数作用是可以实现对列值得拼接: 根据官网介绍,可以对列值排序进行拼接,也可以分组拼接 1.1运行结果 1.2运行结果 2运行结果 注意该函数提供的 over( partition by ...

  2. iptables学习02-nat表应用

    nat表应用实验 第一步 准备工作 A机器两块网卡ens33(192.168.2.106).再添加一块自定义网卡ens37(192.168.100.1),添加到LAN内网区段(自定义名字,写什么无所谓 ...

  3. 滑动表层div时 禁止底层滑动

    $(".container").bind("touchstart", function (events) { startX = events.originalE ...

  4. prometheus服务发现机制

    一. Prometheus与服务发现 1.1 目前支持的服务发现方式 二. 案例 2.1 基于文件的服务发现 2.2 基于Consul的服务发现 三.本地测试 3.1 基于文件的服务发现 1.测试环境 ...

  5. AJAX 的 Ajax返回数据之前的loading等待效果(gif效果等)

    首先,我们通过ajax请求,向后台传递参数,然后后台经过一系列的运算之后向前台返还数据,我希望在等待数据成功返还之前可以展示一个loading.gif图 不废话,在页面上执行点击事件(<a sc ...

  6. js 面向对象中,定义一个函数的过程

    定义一个函数做的两件事:1: 实例化一个Function对象:2: 实例化一个Object对象,并给该函数扩展prototype属性指向这个构造函数 大致过程如图所示: 每一种引用类型(函数,对象,数 ...

  7. webpack压缩图片之项目资源优化

    webpack打包时,会根据webpack.config.js 中url-loader中设置的limit大小来对图片进行处理,小于limit的图片转化成base64格式,其余的不做操作.对于比较大的图 ...

  8. vue安卓4.4.2页面打不开的坑

    项目上线两三天,有保障说安卓下面页面打不开,所以查了下具体原因,系统版本过低,安卓4.4.2,然后发现本地没有babel-polyfill的包,具体解决方案如下: 1,npm 安装 npm insta ...

  9. JavaScript的函数(三)

    函数也是对象,拥有属性和方法,就类似普通对象那样.1,length属性 arguments.lenght 表示传入实参的个数. 函数的length属性时只读属性,代表形参的个数.可以用argument ...

  10. Mysql数据库定时全库备份

    如下脚本用于mysql全库定时备份 mysql_dump_script.sh #!/bin/bash #保存备份个数,最多保留4个文件 number=4 #备份保存路径 backup_dir=/db/ ...