JUC高并发-Condition的使用

  1. 1. 一、Condition的概念
  2. 2. 二、死锁
    1. 2.1. 死锁发生的4个条件
    2. 2.2. 那如何解决这个问题呢?

一、Condition的概念

​ 回忆 synchronized 关键字,它配合 Object 的 wait()、notify() 系列方法可以实现等待/通知模式。

​ 对于 Lock,通过 Condition 也可以实现等待/通知模式。Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,阻塞队列实际上是使用了Condition来模拟线程间协作。

Condition是个接口,基本的方法就是await()和signal()方法;

Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()

调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用Conditon中的await() 对应Object的wait();Condition中的signal() 对应Object的notify();Condition中的signalAll()对应Object的notifyAll()。

二、死锁

Java提供的synchronized也是管程的一种实现,既然Java语言层面已经实现了管程了,为什么还要在SDK里提供另外一种实现呢?

死锁发生的4个条件

  1. 互斥,共享资源X和Y只能被一个线程占用;

  2. 占有且等待,线程T1已经取得共享资源X,在等待共享资源Y的时候,不释放共享资源X;

  3. 不可抢占,其他线程不能强行抢占线程T1占有的资源;

  4. 循环等待,线程T1等待线程T2占有的资源,线程T2等待线程T1占有的资源;

    只有上述4个条件都发生时,才会发生死锁,也就是只要破坏其中的一个,就可以避免死锁的发生。

而synchronized 做不到 破坏“不可抢占”

​ “破坏不可抢占条件”核心是线程能够主动释放它所占有的资源,而synchronized是做不到的;因为synchronized申请资源的时候,如果申请不到,线程直接进入阻塞状态,而进入阻塞状态的线程,啥都干不了,也释放不了已经占有的资源。

那如何解决这个问题呢?

  1. 能够响应中断。synchronized的问题是持有锁A后,如果获取锁B失败,就会进入阻塞状态,一旦发生死锁,就没有机会在唤醒阻塞的线程。如果阻塞状态的线程能够响应中断信号,我们给阻塞的线程发送中断信号的时候,能够唤醒它,那它就有机会释放曾经持有的锁A。这样就破坏了不可抢占条件。
  2. 支持超时。如果线程在一定的时间内没有获取到锁,不是进入阻塞状态,而是返回一个错误,那这个线程也有机会释放曾经持有的锁。也能破坏不可抢占条件。
  3. 非阻塞地获取锁。如果尝试获取锁失败,并不进入阻塞状态,而是直接返回,那这个线程也有机会释放曾经持有的锁。也能破坏不可抢占条件。

这三种方案可以弥补synchronized的问题,也就是Lock接口的三个方法

1
2
3
4
5
6
// 支持中断的API
void lockInterruptibly() throws InterruptedException;
// 支持超时的API
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 支持非阻塞获取锁的API
boolean tryLock();