深入理解ReentrantLock
相对于 synchronized 它具备如下特点  
- 可中断                                                    相关方法:lock.lockInterruptibly()
 
- 可以设置超时时间                                   相关方法:lock.tryLock(1, TimeUnit.SECONDS)
 
- 可以设置为公平锁(防止线程饥饿)             相关方法:new ReentrantLock(true)
 
- 支持多个条件变量                                    相关方法:lock.newCondition()
 
 此外,和synchronized一样,都支持可重入  
 基本语法  
注意:要保证lock和unlock成对出现
 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();     } }
   | 
 
 输出  
17:59:11.862 [main] c.TestReentrant - execute method1  17:59:11.865 [main] c.TestReentrant - execute method2  17:59:11.865 [main] c.TestReentrant - execute method3 
   | 
 
可打断
指的是在锁的时间,其他线程可以用interrupt方法对之进行打断
ReentrantLock lock = new ReentrantLock(); Thread t1 = new Thread(() -> {     log.debug("启动...");     try {          		                  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(); }
   | 
 
 输出  
18:02:40.520 [main] c.TestInterrupt - 获得了锁 18:02:40.524 [t1] c.TestInterrupt - 启动...  18:02:41.530 [main] c.TestInterrupt - 执行打断 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 cn.itcast.n4.reentrant.TestInterrupt.lambda$main$0(TestInterrupt.java:17) at java.lang.Thread.run(Thread.java:748)  18:02:41.532 [t1] c.TestInterrupt - 等锁的过程中被打断
   | 
 
 注意如果是不可中断模式,那么即使使用了 interrupt 也不会让等待中断  
ReentrantLock lock = new ReentrantLock(); Thread t1 = new Thread(() -> {     log.debug("启动...");     lock.lock();     try {         log.debug("获得了锁");     } finally {         lock.unlock();     } }, "t1"); lock.lock(); log.debug("获得了锁"); t1.start(); try {     sleep(1);     t1.interrupt();     log.debug("执行打断");     sleep(1); } finally {     log.debug("释放了锁");     lock.unlock(); }
   | 
 
 输出  
18:06:56.261 [main] c.TestInterrupt - 获得了锁 18:06:56.265 [t1] c.TestInterrupt - 启动...  18:06:57.266 [main] c.TestInterrupt - 执行打断  18:06:58.267 [main] c.TestInterrupt - 释放了锁 18:06:58.267 [t1] c.TestInterrupt - 获得了锁
   | 
 
锁超时
立刻失败
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(); }
   | 
 
 输出  
18:15:02.918 [main] c.TestTimeout - 获得了锁 18:15:02.921 [t1] c.TestTimeout - 启动...  18:15:02.921 [t1] c.TestTimeout - 获取立刻失败,返回
   | 
 
超时失败
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(); }
   | 
 
 输出  
18:19:40.537 [main] c.TestTimeout - 获得了锁 18:19:40.544 [t1] c.TestTimeout - 启动...  18:19:41.547 [t1] c.TestTimeout - 获取等待 1s 后失败,返回
   | 
 
 使用 tryLock 解决哲学家就餐问题  
class Chopstick extends ReentrantLock {     String name;     public Chopstick(String name) {         this.name = name;     }     @Override     public 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;     }     @Override     public 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);     } }
  | 
 
条件变量
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();     } }
   | 
 
 输出  
18:52:27.680 [main] c.TestCondition - 送早餐来了 18:52:27.682 [Thread-1] c.TestCondition - 等到了它的早餐 18:52:28.683 [main] c.TestCondition - 送烟来了 18:52:28.683 [Thread-0] c.TestCondition - 等到了它的烟
   |