一、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个条件
互斥,共享资源X和Y只能被一个线程占用;
占有且等待,线程T1已经取得共享资源X,在等待共享资源Y的时候,不释放共享资源X;
不可抢占,其他线程不能强行抢占线程T1占有的资源;
循环等待,线程T1等待线程T2占有的资源,线程T2等待线程T1占有的资源;
只有上述4个条件都发生时,才会发生死锁,也就是只要破坏其中的一个,就可以避免死锁的发生。
而synchronized 做不到 破坏“不可抢占”
“破坏不可抢占条件”核心是线程能够主动释放它所占有的资源,而synchronized是做不到的;因为synchronized申请资源的时候,如果申请不到,线程直接进入阻塞状态,而进入阻塞状态的线程,啥都干不了,也释放不了已经占有的资源。
那如何解决这个问题呢?
- 能够响应中断。synchronized的问题是持有锁A后,如果获取锁B失败,就会进入阻塞状态,一旦发生死锁,就没有机会在唤醒阻塞的线程。如果阻塞状态的线程能够响应中断信号,我们给阻塞的线程发送中断信号的时候,能够唤醒它,那它就有机会释放曾经持有的锁A。这样就破坏了不可抢占条件。
- 支持超时。如果线程在一定的时间内没有获取到锁,不是进入阻塞状态,而是返回一个错误,那这个线程也有机会释放曾经持有的锁。也能破坏不可抢占条件。
- 非阻塞地获取锁。如果尝试获取锁失败,并不进入阻塞状态,而是直接返回,那这个线程也有机会释放曾经持有的锁。也能破坏不可抢占条件。
这三种方案可以弥补synchronized的问题,也就是Lock接口的三个方法
1 | // 支持中断的API |