从本文开始我们对JUC中比较重要的锁机制进行研究,下面我们来看下java.util.concurrent.locks.Lock接口和其实现类ReentrantLock。
互斥锁--Lock接口及其实现类ReentrantLock:
所谓互斥锁, 指的是一次最多只能有一个线程持有的锁. 在jdk1.5之前, 我们通常使用synchronized机制控制多个线程对共享资源的访问。而现在Lock提供了比synchronized机制更广泛灵活的锁定操作。
对于Lock与synchronized的区别:
1.Lock能够完成synchronized所实现的所有功能,并且提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。
如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断。
如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情。
2、synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定。但Lock不能,只能手动释放,并且在finally从句中unLock()释放。
3、性能:在jdk1.5中Synchronized是使用的独占悲观锁,资源竞争不激烈的情况下synchronized性能略高,资源竞争激烈的情况下性能要远低于Lock。jdk1.6以后synchronized和lock一样都是使用CAS乐观锁操作,所以性能差不多,对于具体使用哪一个要看具体的系统应用需要,Synchronized相对简单易用,若需要精细的灵活控制则可以考虑选择lock。
Lock的主要方法介绍:
void lock(); ---获取锁。执行此方法时,如果锁处于空闲状态,当前线程将获取到锁。相反,如果锁已经被其他线程持有,将禁用当前线程并处于休眠状态,直到当前线程获取到锁。
01. Lock lock = new ReentrantLock();
02. // 获取锁
03. lock.lock();
04. try {
05. // access the resource protected by this lock
06. } finally {
07. // 释放锁
08. lock.unlock();
09. }
void unlock(); ----执行此方法时,当前线程将释放持有的锁。锁只能由持有者释放,如果线程并不持有锁,却执行该方法,可能导致异常的发生。
boolean tryLock(); ----如果锁可用, 则获取锁, 并立即返回true, 否则返回false. 该方法和lock()的 区别在于, tryLock()只是"试图"获取锁, 如果锁不可用, 不会导致当前线程被禁用, 当前线程仍然继续往下执行代码. 而lock()方法则是一定要获取到锁, 如果锁不可用, 就一直等待, 在未获得锁之前,当前线程并不继续向下执行. 通常采用如下的代码形式调用tryLock()方法:
01. Lock lock = new ReentrantLock();
02. if (lock.tryLock()) {
03. try {
04. // manipulate protected state
05. } finally {
06. lock.unlock();
07. }
08. } else {
09. // perform alternative actions
10. }
Condition newCondition(); ----获得与该锁绑定的Condition对象,Condition后面再分析。
void lockInterruptibly() throws InterruptedException; ----如果当前线程未被中断且可用,则获取锁并立即返回。 如果锁不可用,将禁用当前线程,并且在发生以下两种情况之一以前,该线程将一直处于休眠状态:
- 锁由当前线程获得;或者
- 其他某个线程中断当前线程,并且支持对锁获取的中断。