目录
一.ReentrantLock 简介
相对于
synchronized
它具备如下特点:
(1)可中断 : lock.lockInterruptibly 与 thread.interrupt()
(2)可以设置超时时间 :lock.tryLock(long timeout, TimeUnit unit)
(3)可以设置为公平锁/非公平锁 :new ReentrantLock(boolean fair)
公平锁(FairSync):线程获取锁的顺序按照线程加锁的顺序来分配(先来先得)
非公平锁(默认,NonfairSync):获取锁的抢占机制,随机
(4)支持多个条件变量
(5)与 synchronized 一样,都支持可重入
线程再次获取锁:所需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再次获取成功;
锁的最终释放:线程重复n次获取了锁,随后在第n次释放该锁后,其它线程能够获取到该锁。锁的最终释放要求锁对于获取进行计数自增,计数表示当前线程被重复获取的次数,而被释放时,计数自减,当计数为0时表示锁已经成功释放。
基本语法:
// 获取锁
reentrantLock.lock();
try {
// 临界区
} finally {
// 释放锁
reentrantLock.unlock();
}
二.可重入
可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁 如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住。
static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
method1();
}
private static void method1() {
lock.lock();
try {
System.out.println("execute method1");
method2();
} finally {
lock.unlock();
}
}
private static void method2() {
lock.lock();
try {
System.out.println("execute method2");
method3();
} finally {
lock.unlock();
}
}
private static void method3() {
lock.lock();
try {
System.out.println("execute method3");
} finally {
lock.unlock();
}
}
输出:
三.可打断
static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 启动...");
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName() + " 等锁的过程中被打断");
return;
}
try {
System.out.println(Thread.currentThread().getName() + " 获得了锁");
} finally {
lock.unlock();
}
}, "t1");
lock.lock();
System.out.println(Thread.currentThread().getName() + " 获得了锁");
t1.start();
try {
Thread.sleep(1000);
t1.interrupt();
System.out.println(Thread.currentThread().getName() + " 执行了打断");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
输出:
四.锁超时
static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 启动...");
try {
if (!lock.tryLock(1, TimeUnit.SECONDS)) {
System.out.println(Thread.currentThread().getName() + " 获取等待 1s 后失败,返回");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
System.out.println(Thread.currentThread().getName() + " 获得了锁");
} finally {
lock.unlock();
}
}, "t1");
lock.lock();
System.out.println(Thread.currentThread().getName() + " 获得了锁");
t1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
输出:
五.条件变量
synchronized 中也有条件变量,就是
waitSet
休息室,当条件不满足时进入
waitSet
等待。
ReentrantLock 的条件变量比
synchronized
强大之处在于,它是支持多个条件变量的,这就好比
synchronized 是那些不满足条件的线程都在一间休息室等消息
而 ReentrantLock
支持多间休息室,唤醒时也是按休息室来唤醒
使用要点:
(1)await
前需要获得锁
(2)await
执行后,会释放锁,进入
conditionObject
等待
(3)await
的线程被唤醒(或打断、或超时)取重新竞争
lock
锁
(4)竞争
lock
锁成功后,从
await
后继续执行
static ReentrantLock lock = new ReentrantLock();
static Condition waitCigaretteQueue = lock.newCondition();
static Condition waitbreakfastQueue = lock.newCondition();
static volatile boolean hasCigrette = false;
static volatile boolean hasBreakfast = false;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
try {
lock.lock();
while (!hasCigrette) {
try {
waitCigaretteQueue.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 等到了他的烟");
} finally {
lock.unlock();
}
}, "t1").start();
new Thread(() -> {
try {
lock.lock();
while (!hasBreakfast) {
try {
waitbreakfastQueue.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 等到了他的早餐");
} finally {
lock.unlock();
}
}, "t2").start();
Thread.sleep(1000);
sendBreakfast();
Thread.sleep(1000);
sendCigarette();
}
private static void sendCigarette() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 送烟来了");
hasCigrette = true;
waitCigaretteQueue.signal();
} finally {
lock.unlock();
}
}
private static void sendBreakfast() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 送早餐来了");
hasBreakfast = true;
waitbreakfastQueue.signal();
} finally {
lock.unlock();
}
}
输出: