📝个人主页:五敷有你
🔥系列专栏:并发编程
⛺️稳重求进,晒太阳
相对于synchronized 它具备如下特点:
- 可中断
- 可以设置超时时间
- 可以设置公平锁
- 支持多个条件变量
与synchronized一样,都支持可重入
基本语法
//获取锁
reentrantLock.lock();
try{//临界区
}finally{//释放锁reentrantLock.unlock();
}
可重入
可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁
如果是不可重入锁,那么第二次获取锁时,自己也会被锁挡住
static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {method1();
}public static void method1() {lock.lock();try {log.debug("execute method1");method2();} finally {lock.unlock();}}public static void method2() {lock.lock();try {log.debug("execute method2");method3();} finally {lock.unlock();}}public static void method3() {lock.lock();try {log.debug("execute method3");} finally {lock.unlock();}}
输出
可打断
示例
不能用lock()了,用lockInterruptibly()
如果没有竞争,此方法就会获取lock对象
如果有竞争,就进入阻塞队列可以被其他线程用interrupt()方法打断
ReentrantLock lock = new ReentrantLock();Thread t1 = new Thread(() -> {log.debug("启动...");try {//不能用lock()了,用lockInterruptibly()//lock.lockInterruptibly();} catch (InterruptedException e) {e.printStackTrace();log.debug("等锁的过程中被打断");return;}try {log.debug("获得了锁");} finally {lock.unlock();}}, "t1");lock.lock();log.debug("获得了锁");t1.start();try {sleep(1);t1.interrupt();log.debug("执行打断");} finally {lock.unlock();}
运行结果
注意如果是不可中断模式,那么即使使用了 interrupt 也不会让等待中断
锁超时
lock.tryLock() 尝试获取锁,返回boolean
立刻失效
public static void main(String[] args) {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();
}
输出
使用 tryLock 解决哲学家就餐问题
class Chopstick extends ReentrantLock {String name;public Chopstick(String name) {this.name = name;}@Overridepublic String toString() {return "筷子{" + name + '}';}
}class Philosopher extends Thread {Chopstick left;Chopstick right;public Philosopher(String name, Chopstick left, Chopstick right) {super(name);this.left = left;this.right = right;}@Overridepublic void run() {while (true) {// 尝试获得左手筷子if (left.tryLock()) {try {// 尝试获得右手筷子if (right.tryLock()) {try {eat();} finally {right.unlock();}}} finally {left.unlock();}}}}private void eat() {log.debug("eating...");Sleeper.sleep(1);}
}
公平锁(FIFO)
本意:解决饥饿,实际没必要
不公平:当一个线程持有锁,其他线程进入阻塞队列等待,当持有者释放锁时,所有线程开始争夺。
即,后进入的也可能先执行。
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();}// 1s 之后去争抢锁Thread.sleep(1000);new Thread(() -> {System.out.println(Thread.currentThread().getName() + " start...");lock.lock();try {System.out.println(Thread.currentThread().getName() + " running...");} finally {lock.unlock();}}, "强行插入").start();lock.unlock();
强行插入,有机会在中间输出
改为公平锁后
ReentrantLock lock = new ReentrantLock(true);
强行插入,总是在最后输出
公平锁一般没有必要,会降低并发度,后面分析原理时会讲解
条件变量
synchronized中也有条件变量,就是waitSet休息室,当条件不满足时进入waitSet等待
ReentrantLock的条件变量比synchronized强大之处在于,它是支持多个条件变量的。
- synchronized是那些不满足条件的线程都在一间休息室等待消息
- 而ReentrantLock支持多间休息室,有专门等烟的休息室,专门等早餐的休息室,唤醒也是按照休息室唤醒
使用要点:
- await前需要获得锁
- await执行后,会释放出锁,进入conditionObject等待
- await的线程被唤醒(或打断,或超时)取重新竞争lock锁。
- 竞争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) {new Thread(() -> {try {lock.lock();while (!hasCigrette) {try {waitCigaretteQueue.await();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("等到了它的烟");} finally {lock.unlock();}}).start();new Thread(() -> {try {lock.lock();while (!hasBreakfast) {try {waitbreakfastQueue.await();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("等到了它的早餐");} finally {lock.unlock();}}).start();sleep(1);sendBreakfast();sleep(1);sendCigarette();}private static void sendCigarette() {lock.lock();try {log.debug("送烟来了");hasCigrette = true;waitCigaretteQueue.signal();} finally {lock.unlock();}}private static void sendBreakfast() {lock.lock();try {log.debug("送早餐来了");hasBreakfast = true;waitbreakfastQueue.signal();} finally {lock.unlock();}}
输出
下一篇文章:【并发编程】顺序控制&交替输出abc-CSDN博客