Lock接口简介
1.引子
锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源,解决数据的一致性问题。在Lock接口出现之前,Java程序是靠synchronized关键字实现锁功能的,而Java SE 5之后,并发包中新增了Lock接口(以及相关实现类)用来实现锁功能,它提供了与synchronized关键字类似的同步功能,只是在使用时需要显式地获取和释放锁。虽然它缺少了隐式获取释放锁的便捷性,但是却拥有了锁获取与释放的可操作性、可中断的获取锁以及超时获取锁等多种synchronized关键字所不具备的同步特性。
使用synchronized关键字将会隐式地获取锁,当synchronized的方法或代码块执行完成再释放。这种方式简化了同步的管理,可是扩展性没有显示的锁获取和释放来的好。在比较复杂的同步控制中,可操作性不如显式锁来的灵活. 例如,针对一个场景,手把手进行锁获取和释放,先获得锁A,然后再获取锁B,当锁B获得后,释放锁A同时获取锁C,当锁C获得后,再释放B同时获取锁D,以此类推。这种场景下,synchronized关键字就不那么容易实现了,而使用Lock却容易许多。
2.Lock的主要内容
2.1 显式锁Lock的简单使用
和synchronized的同步代码块类似,在“lock.lock()”至“finally“之间的try代码段是锁控制的并发代码片段;lock.lock()进行锁获取,和synchronized代码块生成的字节码关键字“moniterenter”作用类似,而"lock.unlock()"释放锁和synchronized代码块生成“moniterexit”作用类似,其包裹在“finally”保证锁一定会被释放,防止锁一直被线程所持有。
Lock lock = new ReentrantLock(); lock.lock(); try{ //省略锁控制的代码 }finally { lock.unlock(); }
2.2 Lock接口的抽象方法
public interface Lock { //获取锁 void lock(); //响应中断地获取锁 void lockInterruptibly() throws InterruptedException; //尝试获取锁,若获取锁失败,方法立即返回 boolean tryLock(); //在给定的时间内尝试获取锁,在此时间段内仍未获取到锁,最终方法还是将返回 boolean tryLock(long time, TimeUnit unit) throws InterruptedException; //释放锁 void unlock(); /** * 返回一个与当前锁绑定的新Condition实例,可以绑定多个Condition实例<br/> * 此Condition实例用作线程通信,类似于Object的nofify()/wait() */ Condition newCondition(); }
2.3 方法详细描述
void lock():获取锁,当锁获得后,从此方法立即返回
void lockInterruptibly():可中断地获取锁,此方法响应中断,在锁获取过程中若某个时刻当前线程是中断状态,则立即抛出中断异常
tryLock():尝试非阻塞地获取锁,调用此方法若马上能获取到锁,则返回true,若不能立即获取到锁,则立即从方法返回false
tryLock(long time,TimeUnit unit):在给定时间内获取锁,在这3种情况下方法返回①在给定时间内获得了锁,返回true②当前线程被中断,抛出异常,方法返回③超过给定时间,方法返回false。
void unLock():释放锁
Condition newCondition:返回一个与当前锁绑定的新Condition实例
2.4 Lock接口提供的synchronized关键字所不具备的主要特性
特性 | 描述 |
尝试非阻塞地获取锁 | 当前线程尝试获取锁,如果此时锁没被其他线程获取到,则当前线程获取锁成功 |
响应中断地获取锁 | 当获取到锁的线程被中断时,中断异常将抛出,同时释放锁 |
超时地获取锁 | 在给定的时间内获取锁,如果超出给定时间仍未获取到锁,方法仍要返回 |
参考:《Java并发编程的艺术》方腾飞