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

10 阅读 作者:凯哥Java 2020-10-20

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

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

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

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

一:Node节点对象介绍

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

http://img3.sycdn.imooc.com/5e843d1b00013ebb09600488.jpg

简化后:

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

http://img1.sycdn.imooc.com/5e843d1c0001715506030313.jpg

对象中属性介绍

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:当前获取到同步状态的线程对象。

具体可以入下图:

http://img3.sycdn.imooc.com/5e843d1d000112b609600505.jpg

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

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

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

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

http://img3.sycdn.imooc.com/5e843d1d0001704906230171.jpg

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

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

http://img1.sycdn.imooc.com/5e843d1d00019a0309600392.jpg

流程说明:

入队列

入队流程如下:

http://img1.sycdn.imooc.com/5e843d1e0001ea2409600379.jpg

上图流程说明:

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

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

出队列

出队流程图如下:

http://img1.sycdn.imooc.com/5e843d1f0001d80f09600367.jpg

出队流程说明:

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



点击查看更多内容
原文地址:https://www.imooc.com/article/302741
广告一下
热门教程
PHP7报A non well formed numeric value encountered 0
Linux系统下关闭mongodb的几种命令分享 0
mongodb删除数据、删除集合、删除数据库的命令 0
Git&Github极速入门与攻坚实战课程 0
python爬虫教程使用Django和scrapy实现 0
libnetsnmpmibs.so.31: cannot open shared object file 0
数据结构和算法视频教程 0
redis的hash结构怎么删除数据呢? 0
C++和LUA解析器的数据交互实战视频 0
mongodb errmsg" : "too many users are authenticated 0
C++基础入门视频教程 0
用30个小时精通C++视频教程可能吗? 0
C++分布式多线程游戏服务器开发视频教程socket tcp boost库 0
C++培训教程就业班教程 0
layui的util工具格式时间戳为字符串 0
C++实战教程之远程桌面远程控制实战 1
网络安全培训视频教程 0
LINUX_C++软件工程师视频教程高级项目实战 0
C++高级数据结构与算法视频教程 0
跨域问题很头疼?通过配置nginx轻松解决ajax跨域问题 0
相关文章
【译】JavaScript数据结构(3):单向链表与双向链表 16
10个JavaScript难点 16
【译】苹果拒绝支持PWA,有损Web的未来 16
iView 一周年了,同时发布了 2.0 正式版,但这只是开始... 16
nodejs+mongodb构建一个简单登录注册功能 16
【译】JavaScript数据结构(4):树 16
组件化开发与黑箱 16
TypeScript - 不止稳,而且快 16
webpack3+anujs+ReactCSSTransitionGroup 16
原生js实现图片放大镜效果 16
WEB缓存探究第二弹——实战 16
纯笔记:vfork 的一些使用场景(顺便讲一下 fork 的原理) 16
Android APP 内部捐赠实现(支付宝&微信) 16
WKWebView 的一些小总结 16
模型评价(一) AUC大法 16
开始使用GraphQL 16
Webpack模块化原理简析 16
gulp使用问题记录 16
使用Angular4动画为页面添彩 16
Python27 Matplotlib (win64 python2.7) 安装及简单使用 16