Condition----await,signal分析

Condition由来

  在前面synchronized的时候,有讲到wait/notify的基本使用,结合shnchronized
可以实现对线程通信。那么这个时候我就思考了,既然JUC里面提供了锁的实现机制,那么
JUC里面有没有提供类似的线程通信的工具呢?于是找啊找,发现了一个condition工具类

  Condition是一个多线程协调通信工具类,可以让某些线程一起等待某个条件(con
 dition),只有满足条件时,线程才会被唤醒, 案例如下:
public class ConditionDemoWait implements Runnable {


    private Lock lock;


    private Condition condition;


    public ConditionDemoWait(Lock lock, Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }


    @Override
    public void run() {


        System.out.println("begin---ConditionDemoWait");


        try {


            lock.lock();
            condition.await();
            System.out.println("end---ConditionDemoWait");
        } catch (InterruptedException e) {


            e.printStackTrace();


        } finally {


            lock.unlock();
        }
    }
}




public class ConditionDemoSignal implements Runnable {


    private Lock lock;


    private Condition condition;


    public ConditionDemoSignal(Lock lock, Condition condition) {


        this.lock = lock;
        this.condition = condition;
    }


    @Override
    public void run() {


        System.out.println("begin---ConditionDemoSignal");


        try {


            lock.lock();
            condition.signal();
            System.out.println("end---ConditionDemoSignal");


        } finally {


            lock.unlock();
        }


    }
}




public class MainClient {




    public static void main(String[] args) {




        Lock lock = new ReentrantLock();




        Condition condition = lock.newCondition();




        Thread t1 = new Thread(new ConditionDemoWait(lock,condition));
        Thread t2 = new Thread(new ConditionDemoSignal(lock,condition));




        t1.start();
        t2.start();
    }
}

运行结果如下:

begin---ConditionDemoWait
begin---ConditionDemoSignal
end---ConditionDemoSignal
end---ConditionDemoWait

官方解释:

通过这个案例实现了wait()notify()的功能,当调用await方法后,当前线程会释放并等待,而其他线程调用condition对象的signal或者signalAll方法通知被阻塞的线程,然后自己执行unlock释放锁,被唤醒的线程获得之前的锁继续执行,最后释放锁。

所以,condition中有两个最重要的方法

1、await  把当前线程阻塞挂起
2、signal 唤醒等待队列中的线程

其实这样的结果不难理解:

1、首选执行的是ConditionDemoWait这个类,先打印begin---ConditionDemoWait,之后ConditionDemoWait连执行了condition.await()方法
   这个condition.await() 这个方法执行的是释放锁并进入condition的等待队列,类似于wait() 方法

2、当ConditionDemoWait方法释放锁之后,那么相对阻塞的线程ConditionDemoSignal得以获得锁,所以打印begin---ConditionDemoSignal,之后
   ConditionDemoSignal执行condition.signal()方法,这个方法是干嘛的呢?它是唤醒被阻塞的线程(被唤醒的线程可以去竞争锁,但是现在锁没有被释放)
   所以紧接着打印:end---ConditionDemoSignal,当ConditionDemoSignal执行lock.unlock()的时候,表示当前线程执行完毕,锁被释放

3、ConditionDemoWait竞争锁并获取,因此打印end---ConditionDemoWait


Condition 源码分析

调用Condition需要获得Lock锁,所以意味着会存在一个AQS同步队列,在上面那个案例中,假如两个线程同时运行的话,那么AQS的队列可能是下面这种情况

前序:

Thread 用来存放进入AQS队列里面的线程

SHARED 用来标记该线程是获取共享资源是阻塞挂起后放入AQS队列
EXCLUSIVE 用来标记线程是获取独占资源时被挂起后放入AQS队列
waitStatus 记录当前线程等待状态

* CANCELLED 线程取消了

* SIGNAL 线程需要被唤醒

* CONDITION 线程在条件队列里等待

* PROPAGATE 释放共享资源时需要通知其他结点
pre 记录当前结点的前驱结点
next 记录当前结点的后继结点

在这里插入图片描述

解释一下:

当俩线程访问的时候,当线程A获取到锁的时候,那么会修改共享变量state的值,并且将execlusiveOwnerThread的线程改为ThreadA,下面的队列为阻塞队列,等待共享变量state的值为0,接着往下看

那么这个时候ThreadA调用了condition.await()方法,它做了什么事情呢?

condition.await()

调用condition的await()方法(或者以await开头的方法),会使当前线程进入等待队列并释放锁,同时线程状态变为等待状态。当从await()方法返回时,当前线程一定获取了Condition相关联的锁


下面我们来看看await相关的代码:

/**
* Implements interruptible condition wait.
* <ol>
* <li> If current thread is interrupted, throw InterruptedException.
* <li> Save lock state returned by {@link #getState}.
* <li> Invoke {@link #release} with saved state as argument,
*      throwing IllegalMonitorStateException if it fails.
* <li> Block until signalled or interrupted.
* <li> Reacquire by invoking specialized version of
*      {@link #acquire} with saved state as argument.
* <li> If interrupted while blocked in step 4, throw InterruptedException.
* </ol>
*/
public final void await() throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    Node node = addConditionWaiter();
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}

我们沿用老方法先解读注释:

1、可中断的条件等待
2、如果当前线程被中断则抛出异常
3、通过getStatus()获取当前state的值
4、将state作为一个参数当我们去调用release的时候
5、当前线程被阻塞直到同一个lock下执行signal()或者当前线程被中断
6、调用acquire以state作为参数
7、如果在步骤4被中断则抛出异常

下面我们来仔细看下代码的实现:

if (Thread.interrupted())
    throw new InterruptedException();

这一句代码不就是说的,如果当前线程被中断则抛出异常,说的是第一步

Node node = addConditionWaiter();

这句代码也很有意思,回忆一下是不是跟Reentrant里面的addWaiter()有点像呢?

在这里插入图片描述

解释一下:

当俩线程访问的时候,当线程A获取到锁的时候,那么会修改共享变量state的值,并且将execlusiveOwnerThread的线程改为ThreadA,下面的队列为阻塞队列,等待共享变量state的值为0,接着往下看

那么这个时候ThreadA调用了condition.await()方法,它做了什么事情呢?

condition.await()

调用condition的await()方法(或者以await开头的方法),会使当前线程进入等待队列并释放锁,同时线程状态变为等待状态。当从await()方法返回时,当前线程一定获取了Condition相关联的锁


下面我们来看看await相关的代码:

/**
* Implements interruptible condition wait.
* <ol>
* <li> If current thread is interrupted, throw InterruptedException.
* <li> Save lock state returned by {@link #getState}.
* <li> Invoke {@link #release} with saved state as argument,
*      throwing IllegalMonitorStateException if it fails.
* <li> Block until signalled or interrupted.
* <li> Reacquire by invoking specialized version of
*      {@link #acquire} with saved state as argument.
* <li> If interrupted while blocked in step 4, throw InterruptedException.
* </ol>
*/
public final void await() throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    Node node = addConditionWaiter();
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}

我们沿用老方法先解读注释:

1、可中断的条件等待
2、如果当前线程被中断则抛出异常
3、通过getStatus()获取当前state的值
4、将state作为一个参数当我们去调用release的时候
5、当前线程被阻塞直到同一个lock下执行signal()或者当前线程被中断
6、调用acquire以state作为参数
7、如果在步骤4被中断则抛出异常

下面我们来仔细看下代码的实现:

if (Thread.interrupted())
    throw new InterruptedException();

这一句代码不就是说的,如果当前线程被中断则抛出异常,说的是第一步

Node node = addConditionWaiter();

这句代码也很有意思,回忆一下是不是跟Reentrant里面的addWaiter()有点像呢?

场景1: AB两个线程去争抢锁,线程A抢到了锁,之后线程A执行await()如下图所示:

在这里插入图片描述
场景2: 此时B线程开始执行,线程A处于阻塞状态,此时A阻塞在await()方法

在这里插入图片描述

场景3: 此时B线程执行await(),线程A处于阻塞状态,此时A阻塞在await()方法
在这里插入图片描述
场景4: ThreadB正在运行await(),ThreadA被唤醒
在这里插入图片描述
以上就是针对Condition–await()/signal()的解析,谢谢大家,有问题可以指点出来,谢谢大佬!

印象笔记地址: https://app.yinxiang.com/fx/d3c47cab-acc6-41ad-b143-72960cbb082d

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值