ConditionObject是AQS中的内部类,提供了条件锁的同步实现,实现了Condition接口,并且实现了其中的await(),signal(),signalALL()等方法。

  Condition的分析点此

  AbstractQueuedSynchronizer(AQS)的分析点此

  ConditionObject主要是为并发编程中的同步提供了等待通知的实现方式,可以在不满足某个条件的时候挂起线程等待。直到满足某个条件的时候在唤醒线程。

  使用方式如下:

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();//创建和该锁关联的条件锁 public void conditionWait() throws InterruptedException{
lock.lock();
try {
condition.await();
}finally {
lock.unlock();
}
}
public void ConditionSignal() throws InterruptedException{
lock.lock();
try {
condition.signal();
}finally {
lock.unlock();
}
}

  lock()和unlock()在AQS一文中已经分析过其实现方式了,这里主要分析ConditionObject中的await()和signal()的实现分析。

  

  在一个AQS同步器中,可以定义多个Condition,只需要多次lock.newCondition(),每次都会返回一个新的ConditionObject对象。

  在ConditionObject中,通过一个等待队列来维护条线等待的线程。所以在一个同步器中可以有多个等待队列,他们等待的条件是不一样的。

等待队列

  等待队列是一个FIFO的队列,在队列的每个节点都包含了一个线程引用。该线程就是在Condition对象上等待的线程。这里的节点和AQS中的同步队列中的节点一样,使用的都是AbstractQueuedSynchronizer.Node类。每个调用了condition.await()的线程都会进入到等待队列中去。

  在Condition中包含了firstWaiter和lastWaiter,每次加入到等待队列中的线程都会加入到等待队列的尾部,来构成一个FIFO的等待队列。

 

下面看看await()方法的具体实现

 

        public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();  //把当前线程的节点加入到等待队列中
int savedState = fullyRelease(node);  //由于调用await()方法的线程是已经获取了锁的,所以在加入到等待队列之后,需要去释放锁,并且唤醒后继节点线程
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);  //挂起当前线程,当别的线程调用了signal(),并且是当前线程被唤醒的时候才从park()方法返回
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE) //当被唤醒后,该线程会尝试去获取锁,只有获取到了才会从await()方法返回,否则的话,会挂起自己
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}

  可以看到这个方法是会响应中断的。

        private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {  //首先判断lastWaiter节点是否为空,或者是否是处于条件等待,如果不是的话则把它从等待队列中删除。
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)        //把当前线程构建的节点加入到等待队列中去,并且返回当前节点
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
    final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}

  在看看signal()方法的具体实现

        private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&    //从first开始遍历等待队列,把第一个非空、没取消的节点transfer到同步队列
(first = firstWaiter) != null);
} public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}

  signal()方法首先会判断当前线程是不是独占的持有锁,然后唤醒等待队列中的第一个等待线程。

 

    /**
* Transfers a node from a condition queue onto sync queue.
* Returns true if successful.
* @param node the node
* @return true if successfully transferred (else the node was
* cancelled before signal)
*/
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false; /*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
Node p = enq(node);  //返回的是node的前一个节点
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);  //唤醒刚加入到同步队列的线程,被唤醒之后,该线程才能从await()方法的park()中返回。
return true;
}

 

ConditionObject分析的更多相关文章

  1. Java多线程系列 JUC锁07 ConditionObject分析

    ConditionObject ConditionObject是AQS中的内部类,提供了条件锁的同步实现,实现了Condition接口,并且实现了其中的await(),signal(),signalA ...

  2. Java并发基础框架AbstractQueuedSynchronizer初探(ReentrantLock的实现分析)

    AbstractQueuedSynchronizer是实现Java并发类库的一个基础框架,Java中的各种锁(RenentrantLock, ReentrantReadWriteLock)以及同步工具 ...

  3. 【JUC】JDK1.8源码分析之AbstractQueuedSynchronizer(二)

    一.前言 在锁框架中,AbstractQueuedSynchronizer抽象类可以毫不夸张的说,占据着核心地位,它提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架.所以很有必 ...

  4. 【JUC】JDK1.8源码分析之ReentrantLock(三)

    一.前言 在分析了AbstractQueuedSynchronier源码后,接着分析ReentrantLock源码,其实在AbstractQueuedSynchronizer的分析中,已经提到过Ree ...

  5. java分析源码-ReentrantLock

    一.前言 在分析了 AbstractQueuedSynchronier 源码后,接着分析ReentrantLock源码,其实在 AbstractQueuedSynchronizer 的分析中,已经提到 ...

  6. 转载:AbstractQueuedSynchronizer的介绍和原理分析

    简介 提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架.该同步器(以下简称同步器)利用了一个int来表示状态,期望它能够成为实现大部分同步需求的基础.使用的方法是继承,子类通过 ...

  7. Java并发包源码学习之AQS框架(四)AbstractQueuedSynchronizer源码分析

    经过前面几篇文章的铺垫,今天我们终于要看看AQS的庐山真面目了,建议第一次看AbstractQueuedSynchronizer 类源码的朋友可以先看下我前面几篇文章: <Java并发包源码学习 ...

  8. Android ANR分析(2)

    转自:http://blog.csdn.net/ruingman/article/details/53118202   定义 主线程在特定的时间内没有做完特定的事情 常见的场景 A.input事件超过 ...

  9. 通过ReentrantLock源代码分析AbstractQueuedSynchronizer独占模式

    1. 重入锁的概念与作用       reentrant 锁意味着什么呢?简单来说,它有一个与获取锁相关的计数器,如果已占有锁的某个线程再次获取锁,那么lock方法中将计数器就加1后就会立刻返回.当释 ...

随机推荐

  1. LeetCode OJ:Reorder List(重序链表)

    Given a singly linked list L: L0→L1→…→Ln-1→Ln,reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→… You must do thi ...

  2. canvas 遮罩

    上一篇介绍了CSS3可以实现mask的方式,本篇介绍canvas同样也可以实现遮罩的方法: 原理: canvas是在画布上绘图,可以绘制各种形状,同时可以在一个层上重复画图,默认情况下后面的会覆盖前面 ...

  3. Week11《java程序设计》作业总结

    Week11<java程序设计>作业总结 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 答: 2. 书面作业 本次PTA作业题集多线程 1. 源代码 ...

  4. Python中的单例设计模式

    1)设计模式: 是前人工作的总结和提炼.通常,被人们广泛流传的设计模式.     某一问题的特定解决方案,使用设计模式是为了可重用代码,是代码更容易被人理解, 增加代码的可用性. 2)单例设计模式: ...

  5. vue.js 源代码学习笔记 ----- Dep

    /* @flow */ import type Watcher from './watcher' import { remove } from '../util/index' let uid = 0 ...

  6. Java数据封装类

    项目中用到,比较好用!! 用户前端与后台的交互数据,这个封装类中包含分页.数据.错误码.状态码等信息!! 亲测好用哦! 一个类DataWrapper public class DataWrapper& ...

  7. EasyPusher RTSP推流/EasyRTMP RTMP推流Android安卓摄像头视频偏暗的问题解决方案

    本文转自EasyDarwin团队成员JOHN的博客:http://blog.csdn.net/jyt0551/article/details/75730226 在我们测试EasyPusher/Easy ...

  8. Linux操作系统设置SSH及SFTP通过密钥登录

    如果你使用过Linux操作系统的VPS或其他服务器,可能在登录时经常会提示你有多少次登录失败的记录. 这种登录失败的记录实际上也就是攻击者使用脚本自动扫描全网的IP然后进行筛选和测试,最终脚本会使用内 ...

  9. HDU1520 Anniversary party 树形DP基础

    There is going to be a party to celebrate the 80-th Anniversary of the Ural State University. The Un ...

  10. 《selenium2 python 自动化测试实战》(8)——定位iframe

    我们来看一段最早的代码: # coding: utf-8 from selenium import webdriverfrom time import sleep driver = webdriver ...