一、简介
ReentrantLock为可重入锁,整体功能比synchronized更为强大,属于JUC包下的.
与synchronized的区别:
- 可以中断(当一个线程获取synchronized锁后,在执行临界区代码时不能被中断,ReentrantLock可以)
- 可以设置超时时间
- 可以设置为公平锁(synchronized是非公平锁,ReentrantLock支持FIFO)
- 支持多个条件变量(在让线程等待时,可以根据不同条件让线程进入相应的waitSet中等待,唤醒则可以精准唤醒,而不是唤醒所有线程)
相同点:
- synchronized和ReentrantLock都支持可重入.
基本语法:
ReentrantLock reentrantLock = new ReentrantLock();
//获取锁
reentrantLock.lock();
try{
//执行临界区代码
}finally {
//释放锁
reentrantLock.unlock();
}
二、ReentrantLock特性
1.可重入
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
lock.lock();
try{
log.info("{}","执行临界区代码...");
method1();
}finally {
lock.unlock();
}
}
public static void method1(){
lock.lock();
try {
log.info("{}","method1...");
method2();
}finally {
lock.unlock();
}
}
public static void method2(){
lock.lock();
try {
log.info("{}","method2...");
}finally {
lock.unlock();
}
}
输出结果:
20:58:15.801 INFO [main] com.dxf.可重入锁.Test2 - 执行临界区代码...
20:58:15.802 INFO [main] com.dxf.可重入锁.Test2 - method1...
20:58:15.802 INFO [main] com.dxf.可重入锁.Test2 - method2...
上述代码可以看出,一个线程是可以多次获取锁的,所以ReentrantLock支持可重入.否则在执行method2()时线程会进入阻塞状态,不会继续往下执行了.
2.可打断
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
//可打断锁
log.info("尝试获取锁...");
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
log.info("获取锁被打断");
return;
}
try {
//临界区代码
log.info("执行成功...");
}finally {
lock.unlock();
}
}, "t1");
lock.lock();
t1.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//主线程打断
t1.interrupt();
}
执行结果:
21:13:16.820 INFO [t1] com.dxf.可重入锁.Test3 - 尝试获取锁...
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at com.dxf.可重入锁.Test3.lambda$main$0(Test3.java:28)
at java.lang.Thread.run(Thread.java:748)
21:13:17.820 INFO [t1] com.dxf.可重入锁.Test3 - 获取锁被打断
3.锁超时
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
log.info("尝试获取锁...");
//lock.tryLock返回的是布尔值,如果成功获取锁,返回ture,否则返回false
try {
if (!lock.tryLock(2, TimeUnit.SECONDS)) {
log.info("获取锁失败...");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
//执行临界区代码
log.info("获取锁成功");
} finally {
//释放锁
lock.unlock();
}
}, "t1");
lock.lock();
t1.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
log.info("释放锁...");
}finally {
lock.unlock();
}
}
4.公平锁
//参数为true表示开启公平锁
ReentrantLock lock = new ReentrantLock(true)
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
一般不建议使用,会降低并发度.
5.多个条件变量
可以将多个线程做不同的事情区分开来,让他们去各自的休息室等待,当条件满足时,唤醒指定休息室内的线程即可,并不用全部唤醒.
private static ReentrantLock lock = new ReentrantLock();
//等待苹果
private static Condition waitApple = lock.newCondition();
//等待橘子
private static Condition waitOrange = lock.newCondition();
进入waitApple 休息室等待
waitApple.await();
唤醒waitApple休息室中等待的线程
waitApple.signal();
唤醒waitApple休息室中等待的全部线程
waitApple.signalAll();