Java并发笔记 (12)--- Condition与LockSupport 剖析

Condition

任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object上),主要包括wait()wait(long timeout)notify()以及notifyAll()方法,这些方法与synchronized同步关键字配合,以实现等待/通知模式。

Condition接口也提供了类似Object的监视器方法,与Lock配合可以实等待/通知模式,但是这两者在使用方式以及功能特性上还是有差别的。

对比:

对比项Object Monitor MethodsCondition
前置条件获取对象锁调用Lock.lock()
调用Lock.newCondition() 获取Condition对象
调用方法直接调用直接调用
等待队列个数一个多个
当前线程释放锁进入等待状态支持支持
当前线程释放锁进入等待状态,在等待中不响应中断支持不支持
进入超时等待支持支持
进入等待状态到某个时间不支持支持
唤醒等待队列中的一个线程支持支持
唤醒全部线程支持支持

1. Condition接口与示例

直接上示例代码

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void conditionWait() throws InterruptedException {
    lock.lock();
    try{
        condition.await();
    }finally {
        lock.unlock();
    }
}

如示例所示,一般都会将Condition对象作为成员变量。当调用await()方法后,当前线程会释放锁并在此等待,而其他线程调用Condition对象的signal()方法,通知当前线程后,当前线程才从await()方法返回,并且在返回前已经获取了锁。

Condition方法描述:

// 线程进入等待状态,直到被中断或者signal()
void await() throws InterruptedException;
// 进入等待状态,可以看出对中断不敏感
void awaitUninterruptibly();
// 进入等待直到 被中断、signal()、超时
long awaitNanos(long nanosTimeout) throws InterruptedException;
// 与上面相同
boolean await(long time, TimeUnit unit) throws InterruptedException;
// 进入等待直到 被中断、signal()、到某个时间点
boolean awaitUntil(Date deadline) throws InterruptedException;
// 唤醒线程
void signal();
void signalAll();

2. Condition的实现方式

ConditionObject是同步器AbstractQueuedSynchronizer的内部类,可以理解为在同步队列的基础上加了一个等待队列!

1. 等待队列

在这里插入图片描述

等待队列结构如上图所示

注意:述节点引用更新的过程并没有使用CAS保证,原因在于调用await()方法的线程必定是获取了锁的线程,也就是说该过程是由锁来保证线程安全的。

我们下面通过一张图来看看Lock怎么拥有:同步队列和等待队列

在这里插入图片描述

Condition的实现是同步器的内部类,因此每个Condition实例都能够访问同步器提供的方法,相当于每个Condition都拥有所属同步器的引用

2. 等待

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

下面看一下AQSConditionObject类中的await方法:

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);
}

当前线程加入Condition的等待队列的过程如下图:

在这里插入图片描述

3. 通知

调用Condition的signal()方法,唤醒等待时间最长的节点,由上图可以看出等待时间最长的节点是首节点

下面给出 signal()源代码

public final void signal() {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
        // 内部会使用LockSupport.unpark()来唤醒线程竞争同步状态
        doSignal(first);
}

signal()的过程就是将线程从waiting状态->blocked状态的过程

在这里插入图片描述

Condition的signalAll()方法,相当于对等待队列中的每个节点均执行一次signal()方法,效果就是将等待队列中所有节点全部移动到同步队列中,并唤醒每个节点的线程。

补充:LockSupport

LockSupport定义了一组的公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能。

下面从源代码分析一下:

// 阻塞当前线程
public static void park(Object blocker) {
    Thread t = Thread.currentThread();
    setBlocker(t, blocker);
    UNSAFE.park(false, 0L);
    setBlocker(t, null);
}
// 阻塞当前线程 不超过 nanos时间
public static void parkNanos(Object blocker, long nanos) {
    if (nanos > 0) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, nanos);
        setBlocker(t, null);
    }
}
// 阻塞线程至指定时间
public static void parkUntil(Object blocker, long deadline) {
    Thread t = Thread.currentThread();
    setBlocker(t, blocker);
    UNSAFE.park(true, deadline);
    setBlocker(t, null);
}

// 唤醒线程
public static void unpark(Thread thread) {
    if (thread != null)
        UNSAFE.unpark(thread);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值