J.U.C之AQS—Condition

概述

他是一个接口类。

演示例子

结合重入锁ReentrantLock:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Slf4j
public class LockExample6 {

public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();

//执行顺序用数字标出
new Thread(() -> {//线程1
try {
reentrantLock.lock();
log.info("wait signal"); // 1
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("get signal"); // 4
reentrantLock.unlock();
}).start();

new Thread(() -> {//线程2
reentrantLock.lock();
log.info("get lock"); // 2
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
condition.signalAll();
log.info("send signal ~ "); // 3
reentrantLock.unlock();
}).start();
}
}
运行结果
1
2
3
4
09:39:54.468 [Thread-0] INFO com.mmall.concurrency.lock.LockExample6 - wait signal
09:39:54.476 [Thread-1] INFO com.mmall.concurrency.lock.LockExample6 - get lock
09:39:57.476 [Thread-1] INFO com.mmall.concurrency.lock.LockExample6 - send signal ~
09:39:57.476 [Thread-0] INFO com.mmall.concurrency.lock.LockExample6 - get signal
例子分析
  1. 可以看出输出的日志是与我们提到的执行顺序是对应的。
  2. 首先声明定义一个reentrantlock实例,并调用它的newCondition()创建一个condition实例并取出;
  3. 然后当线程1中调用reentrantLock.lock()后,就进入AQS的等待队列中,即之前介绍过的双向列表;
  4. 当调用condition.await()后,线程释放锁并从AQS等待队列中被移除,紧接着就被加入到Condition的等待队列中,该线程再次被唤醒则需要一个signal信号;
  5. 当线程1释放锁,线程2就被唤醒并尝试获取锁。获取后,也加入到AQS的等待队列中。通过调用condition.signalAll()给所有处于condition等待队列的线程发送信号,此时线程1又从Condition等待队列被移除,并加入到AQS等待队列中(还未唤醒)。最后线程2释放锁。
  6. 在线程2释放锁后,线程1拿到锁。此时线程1又处于AQS等待队列的head节点,则又被唤醒,接着上次执行到的地方继续执行后续操作。

源码分析

类内成员

图示


源码大注释:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* {@code Condition} factors out the {@code Object} monitor
* methods ({@link Object#wait() wait}, {@link Object#notify notify}
* and {@link Object#notifyAll notifyAll}) into distinct objects to
* give the effect of having multiple wait-sets per object, by
* combining them with the use of arbitrary {@link Lock} implementations.
* Where a {@code Lock} replaces the use of {@code synchronized} methods
* and statements, a {@code Condition} replaces the use of the Object
* monitor methods.
*
* <p>Conditions (also known as <em>condition queues</em> or
* <em>condition variables</em>) provide a means for one thread to
* suspend execution (to &quot;wait&quot;) until notified by another
* thread that some state condition may now be true. Because access
* to this shared state information occurs in different threads, it
* must be protected, so a lock of some form is associated with the
* condition. The key property that waiting for a condition provides
* is that it <em>atomically</em> releases the associated lock and
* suspends the current thread, just like {@code Object.wait}.
*
* <p>A {@code Condition} instance is intrinsically bound to a lock.
* To obtain a {@code Condition} instance for a particular {@link Lock}
* instance use its {@link Lock#newCondition newCondition()} method.
*/

注释重点:

  1. Condition使得每个对象,因都通过结合他们自己和随意锁的实现,且具有多个等待集合(wait-sets)而被区分开来,从而使监视器(monitor)方法失效。
  2. Conditions(被当做Condition队列)为场景提供一种新处理方式:一个线程可以暂停自己的执行,直到被另一个具有一些可能正确的状态信息的线程通知。由于共享的状态信息的访问发生在不同的线程间,因此它必须被某种形式的锁保护起来。
  3. 该类提供的关键属性是原子性地释放锁,并暂停当前线程。
  4. Condition的实例实际上是一个锁。如果为了一个锁机制而要获取一个Condition实例,需要使用它的newCondition()方法。

类内接口
1. await()

调用该方法会使线程等待直至线程被给予信号量或被中断。

2. await(long time, TimeUnit unit)

指定超时时间参数,或超时时间过期时线程也会被重新调用

3. awaitNanos(long nanosTimeout)

这个方法通过被指定毫微的超时时间,会返回一个线程剩余要等待的毫微时间数。如果超时了的话,该返回值是小于等于0的。

4. awaitUninterruptibly()

该方法是不会产生线程中断情况下的await()方法。

5. awaitUntil(Date deadline)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Causes the current thread to wait until it is signalled or interrupted,
* or the specified deadline elapses.
*
* <p>The lock associated with this condition is atomically
* released and the current thread becomes disabled for thread scheduling
* purposes and lies dormant until <em>one</em> of five things happens:
* <ul>
* <li>Some other thread invokes the {@link #signal} method for this
* {@code Condition} and the current thread happens to be chosen as the
* thread to be awakened; or
* <li>Some other thread invokes the {@link #signalAll} method for this
* {@code Condition}; or
* <li>Some other thread {@linkplain Thread#interrupt interrupts} the
* current thread, and interruption of thread suspension is supported; or
* <li>The specified deadline elapses; or
* <li>A &quot;<em>spurious wakeup</em>&quot; occurs.
* </ul>
*
* <p>In all cases, before this method can return the current thread must
* re-acquire the lock associated with this condition. When the
* thread returns it is <em>guaranteed</em> to hold this lock.
*/
6. signal()

唤醒一个等待中的线程。
该线程在从await()返回前,必须重新请求获取锁。

7. signalAll()

唤醒所有的线程。其中每个线程在从await()返回前,都必须重新请求获取锁。

SupriseMF wechat
欢迎关注微信订阅号【星球码】,分享学习编程奇淫巧技~
喜欢就支持我呀(*^∇^*)~

热评文章