//获取锁
voidlock()//如果当前线程未被中断,则获取锁,可以响应中断
voidlockInterruptibly()//返回绑定到此 Lock 实例的新 Condition 实例
Condition newCondition()//仅在调用时锁为空闲状态才获取该锁,可以响应中断
booleantryLock()//如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁
boolean tryLock(longtime, TimeUnit unit)//释放锁
void unlock()
下面来逐个分析Lock接口中每个方法。lock()、tryLock()、tryLock(long time, TimeUnit unit) 和 lockInterruptibly()都是用来获取锁的。unLock()方法是用来释放锁的。newCondition() 返回 绑定到此 Lock 的新的 Condition 实例 ,用于线程间的协作,详细内容请查找关键词:线程间通信与协作。
1). lock()
在Lock中声明了四个方法来获取锁,那么这四个方法有何区别呢?首先,lock()方法是平常使用得最多的一个方法,就是用来获取锁。如果锁已被其他线程获取,则进行等待。在前面已经讲到,如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此,一般来说,使用Lock必须在try…catch…块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。通常使用Lock来进行同步的话,是以下面这种形式去使用的:
Lock lock =...;
lock.lock();try{//处理任务
}catch(Exception ex){
}finally{
lock.unlock();//释放锁
}
2). tryLock() & tryLock(long time, TimeUnit unit)
tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true;如果获取失败(即锁已被其他线程获取),则返回false,也就是说,这个方法无论如何都会立即返回(在拿不到锁时不会一直在那等待)。
tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false,同时可以响应中断。如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
一般情况下,通过tryLock来获取锁时是这样使用的:
Lock lock =...;if(lock.tryLock()) {try{//处理任务
}catch(Exception ex){
}finally{
lock.unlock();//释放锁
}
}else{//如果不能获取锁,则直接做其他事情
}
3). lockInterruptibly()
lockInterruptibly()方法比较特殊,当通过这个方法去获取锁时,如果线程 正在等待获取锁,则这个线程能够 响应中断,即中断线程的等待状态。例如,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
由于lockInterruptibly()的声明中抛出了异常,所以lock.lockInterruptibly()必须放在try块中或者在调用lockInterruptibly()的方法外声明抛出 InterruptedException,但推荐使用后者,原因稍后阐述。因此,lockInterruptibly()一般的使用形式如下:
public void method() throwsInterruptedException {
lock.lockInterruptibly();try{//.....
}finally{
lock.unlock();
}
}
注意,当一个线程获取了锁之后,是不会被interrupt()方法中断的。因为interrupt()方法只能中断阻塞过程中的线程而不能中断正在运行过程中的线程。因此,当通过lockInterruptibly()方法获取某个锁时,如果不能获取到,那么只有进行等待的情况下,才可以响应中断的。与 synchronized 相比,当一个线程处于等待某个锁的状态,是无法被中断的,只有一直等待下去。
Lock的实现类 ReentrantLock
ReentrantLock,即 可重入锁。ReentrantLock是唯一实现了Lock接口的类,并且ReentrantLock提供了更多的方法。下面通过一些实例学习如何使用 ReentrantLock。
构造方法(不带参数 和带参数 true: 公平锁; false: 非公平锁):
/*** Creates an instance of {@codeReentrantLock}.
* This is equivalent to using {@codeReentrantLock(false)}.*/
publicReentrantLock() {
sync= newNonfairSync();
}/*** Creates an instance of {@codeReentrantLock} with the
* given fairness policy.
*
*@paramfair {@codetrue} if this lock should use a fair ordering policy*/
public ReentrantLock(booleanfair) {
sync= fair ? new FairSync() : newNonfairSync();
}
importjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.ReentrantLock;public classLockThread {
Lock lock= newReentrantLock();public voidlock(String name) {//获取锁
lock.lock();try{
System.out.println(name+ " get the lock");//访问此锁保护的资源
} finally{//释放锁
lock.unlock();
System.out.println(name+ " release the lock");
}
}public static voidmain(String[] args) {
LockThread lt= newLockThread();new Thread(() -> lt.lock("A")).start();new Thread(() -> lt.lock("B")).start();
}
}
从执行结果可以看出,A线程和B线程同时对资源加锁,A线程获取锁之后,B线程只好等待,直到A线程释放锁B线程才获得锁。
总结一下,也就是说Lock提供了比synchronized更多的功能。但是要注意以下几点:
1)synchronized是Java语言的关键字,因此是内置特性,Lock不是Java语言内置的,Lock是一个接口,通过实现类可以实现同步访问。
2)synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中
3)在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态。
ReadWriteLock锁