互斥锁
- JDK1.5版本提供了
java.util.concurrent.locks
包,该包中提供了锁和等待条件的接口和类,可以用于代替JDK1.5之前的synchronized
同步和监视器机制
。 - 互斥锁指的是一次最多只能有一个线程持有的锁。
- 互斥锁在Java中的体现是
Lock接口
和其实现类ReentrantLock
。 - Lock接口的出现主要替代了synchronized关键字的用处,其提供了一个比sychronized机制更广泛的锁定操作
Lock和sychronized机制的主要区别
- synchronized机制提供了对于每个对象相关的
隐式监视器锁
的访问,并强制所有锁获取和释放都要出现在一个块结构中
。 - 当
获取了多个锁
时(多个synchronized代码块嵌套
时),它们必须以相反的顺序释放
。 - synchronized机制对锁的释放是隐式的,只要线程运行的代码
超出了synchronized语句块
范围,持有的锁对象就会自动释放
。 - 锁定和取消锁定出现在不同作用范围中时,必须谨慎地确保保持锁定时所执行的所有代码用 try-finally 或 try-catch 加以保护,以确保在必要时释放锁
- Lock机制必须是显式调用Lock对象的unlock()方法才能释放锁。
- Lock机制可以不在同一个块结构中获取和释放锁,更加自由的释放锁。
Lock接口
Lock
实现提供了使用synchronized
方法和语句所没有的其他功能,包括提供了一个非块结构的获取锁尝试tryLock()
、一个获取可中断锁的尝试lockInterruptibly()
和一个获取超时失效锁的尝试tryLock(long, TimeUnit)
。Lock
类还可以提供与隐式监视器锁完全不同的行为和语义,如保证排序、非重入用法或死锁检测。如果某个实现提供了这样特殊的语义,则该实现必须对这些语义加以记录。- 注意,
Lock
实例只是普通的对象,其本身可以在synchronized
语句中作为目标使用。获取Lock
实例的监视器锁与调用该实例的任何lock()
方法没有特别的关系。为了避免混淆,建议除了在其自身的实现中之外,决不要以这种方式使用Lock
实例。
lock
void lock()
获取锁
- 如果锁处于
空闲
状态,当前线程将直接获取该lock对象锁。 - 相反,如果
锁已经被其他线程持有,将禁用当前线程,直到当前线程获取到锁.
unlock
void unlock()
释放锁
- 当前线程将释放持有的锁。
- 锁只能由持有者释放。
- 如果线程并不持有锁,却执行了该方法,可能会导致异常的发生。
tryLock
boolean tryLock()
仅在调用时锁为空闲状态才获取该锁。
如果锁可用,则获取锁,并返回true
如果锁不可用,立即返回false
此方法可确保如果获取了锁,则会释放锁,如果未获取锁,则不会试图将其释放。即通常配合unlock()使用来释放锁。
1
2
3
4
5
6
7
8
9
10
11Lock lock = new ReentrantLock();
if(lock.tryLock()){
try{
//获取到锁的一些操作
}finally{
//确保了获取锁,才能释放
lock.unlock();
}
}else{
//未获取到锁的一些操作
}
tryLock()
和lock()
方法的区别:tryLock()
方法只是试图获取锁,如果锁不可用,当前线程仍然可以继续往下执行.
lock()
方法是一定要获取到锁,如果锁不可用,就会一直等待下去,锁定当前线程,在未获取指定锁对象之前,当前线程不会继续向下执行
。
Condition接口 - 条件
Condition
将Object
监视器方法wait、notify、notifyAll方法
分解成不同的对象
,为了方便通过将这些对象与任意Lock对象实现组合使用
,为每个对象提供了多个等待set(wait-set)。其中,Lock替代了synchronized方法和语句的使用,Condition替代了Object监视器方法的使用。使用
Condition
对象的相关方法,可以方便的挂起和唤醒线程,而且可以特定的唤醒其其中某个线程
。这也是和Object对象的wait()、notify()、notifyAll()方法的区别。- Object对象的wait()、notify()、notifyAll()方法存在一个问题:如果多个线程调用了obj的wait()方法而挂起,那么我们无法做到调用obj的notify()和notifyAll()方法唤醒其中特定的一个线程,而Conditon对象就可以做到。
- Condition对象
只能通过Lock类的newCondition()方法获取
,因此一个Condition对象必然会有一个与其绑定的Lock锁
。
await
void await()
造成当前线程再接到信号或被中断之前一直处于等待状态
- 将当前线程处于
等待
状态,并释放
该Condition对象所绑定的锁
. - 使用
await()方法前
,当前线程必须持有与该Condition对象绑定的锁
,否则程序可能会抛出异常。
signal
void signal()
唤醒一个在该Condition对象上挂起的线程
- 如果存在
多个线程同时等待
该Condition对象的唤醒,则随机选择其中一个
唤醒。 - 线程
被唤醒之前,必须重新获取到锁
,即与该Condition对象绑定的Lock对象。
signalAll
void signalAll()
唤醒所有
在该Condition对象上挂起的线程
- 所有被唤醒的线程将竞争与该Condition对象绑定的锁,只有获取到锁的线程才能恢复到运行状态。
一个实例
1 | 问题的描述: |
利用ReentrantLock
和Condition
接口组合,可以轻松指定和分配各个线程该完成的操作。代码如下:
1 |
|