Lock 接口
自Java 5之后,java.util.concurrent.locks 包中的 Lock 接口提供了一种新的方式来实现同步访问。
Lock 接口提供了与 synchronized 类似的同步功能,但需要在使用时手动获取和释放锁。此外,Lock 接口更为灵活,提供了以下 synchronized 接口所不具备的特性:
- 超时获取锁:在指定时间内获取锁,超过时间仍未获得锁则返回
- 公平锁:可以通过构造方法 ReentrantLock(boolean fair) 选择是否使用公平锁
- 响应中断:线程在等待获取锁的过程中可以响应中断,并抛出中断异常
- 支持绑定多个条件:可以通过绑定 Condition 对象来判断当前线程通知的是哪些线程
Lock 接口中的主要方法:
- void lock():获取锁。如果锁已被其它线程获取,进行等待
- void unlock():释放锁
- boolean tryLock():尝试获取锁。如果获取成功,返回true,否则返回false
- boolean tryLock(long time, TimeUnit unit) throws InterruptedException:在指定时间内尝试获取锁。如果获取成功,返回true,否则返回false
- void lockInterruptibly() throws InterruptedException:获取锁。如果锁已被其它线程获取,进行等待。在等待过程中,能够响应中断,中断线程的等待状态
重入锁ReentrantLock
Lock接口主要的实现类为重入锁 ReentrantLock。
重入锁的意思可以打个比方:如果你获得了某个房间的钥匙,在还钥匙之前你可以无限次的进入这个房间,synchronized 和 ReentrantLock 都属于可重入锁。
使用示例:
public class RLockDemo {
//默认构造函数是非公平锁
Lock lock = new ReentrantLock();
public void f() {
lock.lock();
try {
System.out.println("时间:" + LocalTime.now().withNano(0));
TimeUnit.SECONDS.sleep(2);
System.out.println("hello,world!");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("时间:" + LocalTime.now().withNano(0));
System.out.println("==============");
lock.unlock();
}
}
public static void main(String[] args) {
RLockDemo demo = new RLockDemo();
for (int i = 0; i < 2; i++) {
new Thread(() -> {
demo.f();
}).start();
}
}
}
输出结果:
时间:21:54:26
hello,world!
时间:21:54:30
==============
时间:21:54:30
hello,world!
时间:21:54:34
==============
可以看到,当第一个线程释放了锁后,第二个线程立刻获得了锁。
注意:采用 Lock 时,必须主动释放锁。因为在发生异常时,不会自动释放锁,因此需要将释放锁的操作放在 finally 块中进行,以保证锁一定会被释放,防止死锁的发生。
参考链接: