ReentrantLock
相对于 synchronized 它具备如下特点:
-
可中断
-
可以设置超时时间
-
可以设置为公平锁
-
支持多个条件变量
可重入
与 synchronized 一样,都支持可重入
可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁 如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住
@Slf4j
public class Test04 {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
method1();
}
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();
}
}
}
可打断
lock.lockInterruptibly()
设置可打断锁:
@Slf4j
public class Test04 {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
log.info("等待过程被打断");
return;
}
try {
log.info("method1开始执行。。。。");
} finally {
lock.unlock();
}
},"t1");
lock.lock();
log.info("主线程获取到锁");
t1.start();
try {
Thread.sleep(1000);
log.info("执行打断");
t1.interrupt();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
锁超时
立刻失败:
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
log.debug("启动...");
if (!lock.tryLock()) {
log.debug("获取立刻失败,返回");
return;
}
try {
log.debug("获得了锁");
}
finally {
lock.unlock();
}
}, "t1");
lock.lock();
log.debug("获得了锁");
t1.start();
try {
sleep(2);
} finally {
lock.unlock();
}
超时失败:
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
log.debug("启动...");
try {
if (!lock.tryLock(1, TimeUnit.SECONDS)) {
log.debug("获取等待 1s 后失败,返回");
return;
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
try {
log.debug("获得了锁");
}
finally {
lock.unlock();
}
}, "t1");
lock.lock();
log.debug("获得了锁");
t1.start();
try {
sleep(2);
} finally {
lock.unlock();
}
公平锁
ReentrantLock 默认是不公平的
ReentrantLock lock = new ReentrantLock(false);
lock.lock();
for (int i = 0; i < 500; i++) {
new Thread(() -> {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " running...");
}
finally {
lock.unlock();
}
},
}
"t" + i).start();
finally {
lock.unlock();
// 1s 之后去争抢锁
Thread.sleep(1000);
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " start...");
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " running...");
}
}
}, "强行插入").start();
lock.unlock();
强行插入,有机会在中间输出
t39 running...
t40 running...
t41 running...
t42 running...
t43 running...
强行插入 start...
强行插入 running...
t44 running...
t45 running...
t46 running...
t47 running...
t49 running...
改为公平锁后
ReentrantLock lock = new ReentrantLock(true);
强行插入,总是在最后输出
t465 running...
t464 running...
t477 running...
t442 running...
t468 running...
t493 running...
t482 running...
t485 running...
t481 running...
强行插入 running...
但公平锁一般没有必要,会降低并发度
条件变量
ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的
@Slf4j
public class Test04 {
private static ReentrantLock lock = new ReentrantLock();
static Condition condition1 = lock.newCondition();
static Condition condition2 = lock.newCondition();
static volatile boolean hasCigrette;
static volatile boolean hasTakeout;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
lock.lock();
try {
while(!hasCigrette){
try {
condition1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("等到了Cigrette");
} finally {
lock.unlock();
}
},"t1").start();
new Thread(() -> {
lock.lock();
try {
while(!hasTakeout){
try {
condition2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("等到了Takeout");
} finally {
lock.unlock();
}
},"t2").start();
Thread.sleep(1000);
sendCigrette();
Thread.sleep(2000);
sendTakeout();
}
private static void sendCigrette(){
lock.lock();
try {
log.info("正在送Cigrette。。。");
hasCigrette = true;
condition1.signal();
} finally {
lock.unlock();
}
}
private static void sendTakeout(){
lock.lock();
try {
log.info("正在送Takeout");
hasTakeout = true;
condition2.signal();
} finally {
lock.unlock();
}
}
}