Java并发基础-Condition对象分析

Condition即条件,它用于线程检测条件是否满足,从而决定当前线程是否挂起或者继续执行;这个条件可以被其它线程所更新,并且在更新后通知挂起线程可以继续处理。如最经典的生产者与消费者模式,就可以通过Condition很方便的实现。

本文目录:

1 基础

1.1 Condition对象简单使用

1.1.1 Condition对象的创建

Condition需要通过Lock对象的newCondition方法创建,其使用示例如下:

Lock lock = new ReentrantLock();
Condition emptyCondition = lock.newCondition();

说明
由于Condition一定会在多线程环境中使用,因此它需要与一个Lock关联起来保证其在多线程环境下的安全性。创建某个Lock相关联的Condition对象,需要使用该Lock的newCondition方法。

1.1.2 Condition对象的使用

先看下下面的用法,检测某个条件不满足时通过await方法阻塞当前线程: 

lock.lock();
try {
    while (!b){
        emptyCondition.await();
    }
} finally {
    lock.unlock();
}

如果B条件不满足,那么当前线程将会一直等待。直到某个线程调用了同一个Condition对象的signal或者signalAll方法:

lock.lock();
try {
    b = true;
    emptyCondition.signalAll();
} finally {
    lock.unlock();
}

说明

  • 通过上述代码,可以看到在调用Condition的await和signalAll方法前都需要对Condition关联的Lock对象进行加锁,这是因为这两段代码的两个线程实际操作的是同一个b对象和同一个Condition对象,为保证线程安全性必须进行加锁处理。
  • 必须使用try-finally将操作封装,并且在finally中必须调用lock.unlock方法;否则如果在调用lock.unlock前发生异常,那么锁将不会被释放,最终将会导致死锁!

1.2 虚假唤醒(spurious wakeup)

虚假唤醒指的是,一个等待的线程即使没有其它线程调用signal方法也可能被唤醒,这个时候原始的条件未能满足;因此,在检测相关的条件时,需要使用while而不是if来进行判断,如1.1.2中对b条件是否成立的判断,如果使用的是if,那么当线程被虚假唤醒时,将误以为b条件已经满足而实际上未满足,这将导致后续依赖于该条件成立的处理出现异常。

关于虚假唤醒的具体说明,请参考https://en.wikipedia.org/wiki/Spurious_wakeup

2 Condition对象深入分析

Condition对象本身包含了以下方法:

2.1 void await() throws InterruptedException

使得当前线程挂起,直到下面某个事件发生:

  • 其它线程调用了当前的Condition对象的signal方法或者signalAll方法;
  • 其它线程中断了当前线程;此时await方法将会抛出InterruptedException,当前线程继续执行;
  • 当前线程被虚假唤醒

当线程挂起时,它所获得的锁对象将会被释放;因此当它被唤醒继续运行时,会自动的重新获取Condition关联的锁。

说明
为什么在挂起时要释放它已经获得的锁,是因为如果它不释放的话,那么其它线程将没有办法获取到对应的锁,如1.1.2中的示例,将会导致线程2一直等待被线程1占用的锁; 而线程1在等待B被线程2设置成True;这个时候就必然会导致死锁。

2.2 void awaitUninterruptibly()

使得当前线程挂起,直到下面某个事件发生:

  • 其它线程调用了当前的Condition对象的signal方法或者signalAll方法;
  • 当前线程被虚假唤醒

它与await不同之处在于它不能被其它线程中断,因此也不会抛出中断异常。

2.3 long awaitNanos(long nanosTimeout) throws InterruptedException

使得当前线程挂起,直到下面某个事件发生:

  • 其它线程调用了当前的Condition对象的signal方法或者signalAll方法;
  • 其它线程中断了当前线程;此时将会抛出InterruptedException,当前线程继续执行;
  • 当前线程被虚假唤醒
  • 指定的超时时间到,超时时间单位为纳秒。

这个方法与前面的方法不一样的地方是它有一个返回值,其表示的意义,JAVADOC中说明如下:The method returns an estimate of the number of nanoseconds remaining to wait given the supplied {@code nanosTimeout} value upon return, or a value less than or equal to zero if it timed out. This value can be used to determine whether and how long to re-wait in cases where the wait returns but an awaited condition still does not hold。这段理解上有些问题,网上中文的写法感觉也都不是很清晰。后续看有没有办法查到更加详细的资料。

2.4 boolean await(long time, TimeUnit unit) throws InterruptedException

与2.3中awaitNanos类似,要等待的事件也一样。
不一样的地方是它的返回值:如果超时时间到了后未成功,则返回False,否则返回True;

2.5 boolean awaitUntil(Date deadline) throws InterruptedException

与awaitNanos类似,但它不是根据超时时间来决定是否中止等待的,而是等待到指定的时间。

2.6 void signal()

唤醒某个被await类的方法挂起的线程。如果有多个线程挂起则会选择其中的一个。

2.7 void signalAll()

唤醒所有的挂起线程;如果有多个挂起线程,每一个线程都要获得关联的Lock对象的锁才能继续执行。

参考文献

猜你喜欢

转载自blog.csdn.net/icarusliu/article/details/79573074