简介
ReentrantLock是java提供的另外一种锁,是一种可重入、可中断、可公平锁、非公平锁,它是一个类实现了Lock接口,下面介绍它的使用方式及常用的方法。
一、ReentrantLock的基本使用
下面是创建一个锁然后获取再释放的一个过程,可以看到需要调用unlock()方法释放锁,为了防止程序发生异常导致不能释放锁造成程序的死锁,一般要把释放锁的代码写在finally块中,确保锁的释放。
public class Test02 {
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args){
try {
// 获取锁
lock.lock();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放锁
lock.unlock();
}
}
}
二、ReentrantLock的可重入
public class Test02 {
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args){
// 获取锁
lock.lock();
try {
System.out.println("main方法中获取到了锁..");
m1();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放锁
lock.unlock();
System.out.println("main方法中释放到了锁..");
}
}
public static void m1(){
lock.lock();
try {
System.out.println("m1方法中获取到了锁..");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放锁
lock.unlock();
System.out.println("m1方法中释放了锁..");
}
}
}
上面的代码和输入结果证明,在一个线程中,main方法没有释放锁,m1方法就能获取到锁,由此证明了ReentrantLock的可重入性。
三、ReentrantLock的可打断
要想实现可打断特性,不可以调用lock()方法,需要调用 lockInterruptibly() 方法。当有线程竞争锁的时候,某一线程获取锁失败会进入休眠状态,如果加锁时调用的是lockInterruptibly() 方法,在其它线程中调用interrupt方法,该线程会报异常并放弃争抢锁,如果加锁时调用lock方法,interrupt后依然会继续休眠,直到获取到锁。
四、获取锁锁超时 --tryLock()
加锁时调用tryLock()方法会返回获取锁的结果,可通过返回的结果判断要执行的逻辑。带参数的tryLock方法可以传进去等待的时间 例如:lock.tryLock(1, TimeUnit.SECONDS); 获取锁失败会重试,1s后依然没获得锁才会走失败的逻辑。
Thread t1 = new Thread(() -> {
try {
// 获取锁
boolean b = lock.tryLock(1, TimeUnit.SECONDS);
if (b){
System.out.println("t1线程获取到了锁..");
}else {
System.out.println("获取锁失败");
return;
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("t1线程被打断了,停止获取");
return;
} finally {
// 释放锁
lock.unlock();
System.out.println("t1线程释放了锁..");
}
});
上面的代码演示了带参数的tryLock的使用,另外,获取锁的过程中可以被打断,所以需要在catch块中写入被打断后的逻辑。
五、 ReentrantLock的公平锁
ReentrantLock默认非公平,ReentrantLock lock = new ReentrantLock(true); 可以设置为公平锁,公平锁会降低并发度,因此推荐使用非公平锁。
六、ReentrantLock条件变量的使用
ReentrantLock支持多个条件变量,Condition condition = lock.newCondition(); 这样就是可以创建一个条件变量,调用await()方法,可以让线程进入该等待室中休息,调用condition.signal();可以唤醒该等待室中的一个线程。