AQS

内部类Node

等待队列是CLH有锁队列的变体。

waitStatus的几种状态:

  static final int CANCELLED =  1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;

以下面的测试程序为例,简单介绍一下同步队列的变化:

	@Test
public void test() {
CountDownLatch countDownLatch = new CountDownLatch(1);
ReentrantLock lock = new ReentrantLock();
try {
for (int i = 0; i < 5; i++) { new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
}
}, "线程 " + i
).start();
} countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// lock.unlock();
}

我们发现,ReentrantLock的lock方法如下:

		final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}

由于是独占的获取,因此只有一个线程会通过CAS成功获取state,因此其它四个线程都会进入acquire(1)方法。acquire(int arg)是AQS的模板方法,方法内容如下:

	public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}

以非公平锁为例,tryAcquire实际调用nonfairTryAcquire.该方法可以看出,首先还是通过CAS来获取state,如果是owner是之前的那个线程的话,允许重入,acquire加acquires。

		final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}

继续回到刚才的acquire方法,会发现tryAcquire方法返回false,调用addWaiter方法:

	private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}

假设最开始是线程0获取了state,后面来的依次是线程1、线程2、线程3、线程4.

线程1进入addWaiter方法,tail为空,进入enq方法,这里会初始化AQS中的head和tail,例子里的话head是一个new Node对象,tail的Node对象是new Node(“线程1”, mode)对象。

	private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}

继续,执行完addWaiter方法之后会进入acquireQueued方法:

	final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 找到node的前辈节点
final Node p = node.predecessor();
// 如果线程0不释放,则该不会进入
// 如果线程0释放state,并且p是head,也就是同步队列中的第一个任务,这个时候获取state成功,将node设置为AQS的head,返回false,结束acquire方法。
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 第一个判断,判断node的前辈节点是否为-1或者大于0,否则设置状态为-1,再下一次循环时,返回true进入第二个判断
// 第二个判断,将node对应的线程park,即设置为wait状态
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}

其余的线程2/3/4依次在同步队列上,类似于:

+-----------+ +-----------+ + -----------+ + -----------+ +-----------+

| head | | 线程1 | | 线程2 | | 线程3 | | 线程4|

+-----------+ +-----------+ + -----------+ + -----------+ +-----------+

以下面测试程序为例,再看unlock方法(顺便提一下,idea调试多线程需要将断点处的all改为thread, 程序中的countdownlatch是为了不让test线程结束,导致无法调试)调试时看到一个线程进入release方法,其余四个线程处于wait状态,说明程序正确了。

	@Test
public void test() {
CountDownLatch countDownLatch = new CountDownLatch(1);
ReentrantLock lock = new ReentrantLock();
try {
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if ( lock.isHeldByCurrentThread()) { lock.unlock();
}
}
}, "线程 " + i
).start();
} countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
}
}

讲完了lock()方法,再看unlock()方法,调用release(int arg)方法

	public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}

tryRelease(arg)不再赘述,不过是释放获得的许可,将state设置为0(一般情况下,有些是重入,需要多调用几次unlock才行),置空独占线程。

进入if内部,调用unparkSuccessor方法

	private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); /*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}

正常情况下,唤醒同步队列中的第一个任务线程

acquireShared

上面讲的是独占获取,接下来看一下共享获取

这里以ReentrantReadWriteLock为例

简单介绍一下内部类,包含一个同步器Sync,以及公平及非公平类FairSync与NonfairSync,ReadLock和WriteLock

因为读锁非独占,因此lock方法对应的是sync.tryAcquireShared(1),写锁则相反。

其他

AQS使用了模板方法设计模式。

并发编程(4)——AbstractQueuedSynchronizer的更多相关文章

  1. 并发编程 20—— AbstractQueuedSynchronizer 深入分析

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  2. Java并发编程系列-AbstractQueuedSynchronizer

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/10566625.html 一.概述 AbstractQueuedSynchronizer简 ...

  3. Java并发编程(2) AbstractQueuedSynchronizer的设计与实现

    一 前言 上一篇分析AQS的内部结构,其中有介绍AQS是什么,以及它的内部结构的组成,那么今天就来分析下前面说的内部结构在AQS中的具体作用(主要在具体实现中体现). 二 AQS的接口和简单示例 上篇 ...

  4. Java并发编程(2) AbstractQueuedSynchronizer的内部结构

    一 前言 虽然已经有很多前辈已经分析过AbstractQueuedSynchronizer(简称AQS,也叫队列同步器)类,但是感觉那些点始终是别人的,看一遍甚至几遍终不会印象深刻.所以还是记录下来印 ...

  5. 并发编程 01—— ThreadLocal

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  6. 并发编程 02—— ConcurrentHashMap

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  7. 并发编程 04——闭锁CountDownLatch 与 栅栏CyclicBarrier

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  8. 并发编程 05—— Callable和Future

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  9. 并发编程 06—— CompletionService :Executor 和 BlockingQueue

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  10. 并发编程 10—— 任务取消 之 关闭 ExecutorService

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

随机推荐

  1. 设计模式-抽象工厂模式(AbstractFactory)

    抽象工厂模式是向客户端提供一个接口(FruitFactory),,使得客户端在不必指定产品的具体类型的情况下,能够创建多个产品族(NorthFruit.SouthFruit)的产品对象. 角色和职责: ...

  2. Git 安装教程(2.21.0)

    获取安装包地址 1.官网(较慢):https://git-scm.com/downloads 2.下载链接:http://down-ww3.newasp.net/pcdown/soft/soft1/g ...

  3. linux命令积累

    lsof -i: //根据端口号查相关信息 //杀进程 ps -ef|grep appName //根据进程名称查找相关信息 grep -r "关键词" 目录 //在制定目录下根据 ...

  4. c++学习书籍推荐《C++ Primer Plus中文版(第6版)》下载

    百度云及其他网盘下载地址:点我 编辑推荐 一本经久不衰的C++畅销经典教程:一本支持C++11新标准的程序设计图书.  它被誉为“开发人员学习C++的教程,没有之一”! Amazon网站“Langua ...

  5. markdown的博客

    测试一下markdown写博客 function firstProgram() { console.log("This is my first markdown blog"); }

  6. Windows 下配置 Logstash 为后台服务

    到目前为止,logstash 没有给出官方的,在 windows 系统中作为后台服务运行的方式.本文将介绍如何使用第三方工具 nssm 让 logstash 作为后台服务运行在 windows 中.说 ...

  7. C程序中文标点惹的祸,你可长点儿心吧

    想必你在编程时,即使写了一个很小很简短的程序,本以为编译一定不会有问题,自信满满,结果编译完了,编译器 给了几个大大的error,比如:error C2018: unknown character'0 ...

  8. sublime 如何安装插件实现高效输入,如何支持markdown 语法,并实时预览

    啊,一直想鼓捣markdown的语法,但是配置什么的有点麻烦.不过用起来的话,真心顺手.无需考虑格式与语法点来点去影响效率, 用心去搬砖,用脚修bug 一.初识 Package Control 首先, ...

  9. 个人永久性免费-Excel催化剂功能第34波-提取中国身份证信息、农历日期转换相关功能

    这两天又被刷朋友圈,又来了一个自主研发红芯浏览器,国产啊国产,这是谁的梦.就算国产了,自主了,无底线的夸大吹嘘无道德,企业如是,国家如是,大清已亡!再牛B的技术落在天天删敏感信息.无法治.无安全感可言 ...

  10. Docker部署web环境之Lanmp

    1. 案例一 整套项目多容器分离通过docker-compose部署lanmp环境 中方文档参考网址: docker/kubernets网址 http://www.dockerinfo.net/doc ...