可重入锁ReentrantLock基础和原理

可重入锁ReentrantLock基础和原理 ReenterantLock基础ReenterantLock是synchronized关键字的扩展。它是一个显示锁意味着锁的获取和释放必须由程序员手动编写代码控制。相对于synchronized它具备以下的优势未获取锁后的状态可中断可以设置超时时间可以设置公平锁支持多个条件变量并且它与synchronized一样都支持可重入。可重入可重入是指同一个线程如果首次获得这把锁后因为它是这把锁的主人可以再次获得这把锁。相对的不可重入是指获得这把锁之后不能再次获得这把锁自身也会被锁挡住。public static void main(String[] args) throws ExecutionException, InterruptedException { ReentrantLock reentrantLocknew ReentrantLock(); for (int i0;i100;i){ testReentrantLock(reentrantLock,i); } } public static void testReentrantLock(ReentrantLock reentrantLock,int number){ reentrantLock.lock(); System.out.printf(第%d次获得锁\n,number); }未获得锁后的状态可打断先看synchronized的行为。public static void main(String[] args) throws ExecutionException, InterruptedException { Object locknew Object(); Thread thread1new Thread(()-{ synchronized (lock){ while(true){ } } }); thread1.start(); TimeUnit.SECONDS.sleep(1); Thread thread2new Thread(()-{ synchronized (lock){ } System.out.println(running); }); thread2.start(); TimeUnit.SECONDS.sleep(1); System.out.println(线程1:thread1.getState()); System.out.println(线程2:thread2.getState()); thread2.interrupt(); System.out.println(线程2:thread2.getState()); }程序运行结果如下再看ReentrantLock的lock行为。public static void main(String[] args) throws ExecutionException, InterruptedException { ReentrantLock locknew ReentrantLock(); Thread thread1new Thread(()-{ lock.lock(); while(true){ } }); thread1.start(); TimeUnit.SECONDS.sleep(1); Thread thread2new Thread(()-{ try { lock.lockInterruptibly(); } catch (InterruptedException e) { System.out.println(interrupted); return ; } System.out.println(running); lock.unlock(); }); thread2.start(); TimeUnit.SECONDS.sleep(1); System.out.println(线程1:thread1.getState()); System.out.println(线程2:thread2.getState()); thread2.interrupt(); System.out.println(线程2:thread2.getState()); }结果如下可以看出通过synchronized获取锁失败后是进入BLOCKED状态而通过ReentrantLock获取锁失败后是进入WAITTING状态。接下来继续观察lockInterruptiblypublic static void main(String[] args) throws ExecutionException, InterruptedException { ReentrantLock locknew ReentrantLock(); Thread thread1new Thread(()-{ lock.lock(); while(true){ } }); thread1.start(); TimeUnit.SECONDS.sleep(1); Thread thread2new Thread(()-{ try { lock.lockInterruptibly(); } catch (InterruptedException e) { System.out.println(interrupted); return ; } // lock.lock(); System.out.println(running); lock.unlock(); }); thread2.start(); TimeUnit.SECONDS.sleep(1); System.out.println(线程1:thread1.getState()); System.out.println(线程2:thread2.getState()); thread2.interrupt(); System.out.println(线程2:thread2.getState()); }结果是先探究一个问题为什么BLOCKED不可以被打断WAITING可以被打断我们知道interrupt()是Java线程中用于协作式地通知一个线程应该停止当前正在执行的任务的核心机制。它的核心作用是设置线程的中断标志位而不是强制终止线程。线程状态调用t.interrupt()的影响RUNNABLE仅设置中断标志位。线程的isInterrupted()将返回true但线程会继续运行直到它自己检查这个标志。BLOCKED(等锁)仅设置中断标志位。线程依然会卡在BLOCKED状态继续等它的锁。不会抛出InterruptedException。直到它终于拿到锁进入RUNNABLE后才能通过检查标志位来响应中断。WAITINGTIMED_WAITING(等通知、睡眠、合并)立即抛出InterruptedException并且清除中断标志位重置为false。线程会从wait(),join(),sleep()等方法调用中退出进入RUNNABLE状态。那为什么ReentrantLock的lock也是WAITING状态为什么不会被打断呢因为导致WAITING状态的原因和底层机制不同ReentrantLock的加锁底层就是LockSupport.park()进入WAITING的方法对interrupt()的响应说明Object.wait()Thread.sleep()Thread.join()​立即响应抛出InterruptedException。并清除中断状态这些是Java标准库提供的协作式中断原语设计目的就是快速响应中断请求。LockSupport.park()​立即解除阻塞但不抛出异常只是安静地返回。但是保留中断状态这是更底层的线程阻塞原语。中断会唤醒它但行为的控制权完全交给了上层的调用代码。ReentrantLock就构建在此之上。也就是说其实interrupt是唤醒了线程的但是它被唤醒之后再次去尝试获得锁导致又调用了LockSupport.lock。而ReentrantLock.lockInterruptibly只是设定了唤醒之后抛出异常这部分在后面的源码部分再深挖。锁超时通过synchronized获得锁时如果未获得则会一直BLOCKED而ReentrantLock可以设置超时的时间。public static void main(String[] args) throws ExecutionException, InterruptedException { ReentrantLock locknew ReentrantLock(); Thread thread1new Thread(()-{ lock.lock(); while(true){ } }); thread1.start(); TimeUnit.SECONDS.sleep(1); Thread thread2new Thread(()-{ // try { // lock.lockInterruptibly(); // } catch (InterruptedException e) { // System.out.println(interrupted); // return ; // } try { lock.tryLock(5,TimeUnit.SECONDS); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(running); //lock.unlock(); }); thread2.start(); TimeUnit.SECONDS.sleep(1); System.out.println(线程1:thread1.getState()); System.out.println(线程2:thread2.getState()); //thread2.interrupt(); System.out.println(线程2:thread2.getState()); }结果如下使用tryLock()再尝试在给定的时间内获取锁如果没有获得将直接进行剩余代码。公平锁synchronized是非公平锁ReentrantLock默认也是非公平锁。 以下代码启用公平锁ReentrantLock locknew ReentrantLock(true);支持多个条件变量synchronized中存在唯一的一个条件变量也就是waitSet任何调用wait的线程都将进入waitSet通过notifyAll将会唤醒waitSet中的所有线程。而ReentrantLock支持多个条件变量。可以实现多个类似的waitSet实现一种精细化的唤醒。方法作用说明await()使当前线程等待释放锁进入等待状态signal()唤醒一个等待线程从等待队列中唤醒一个signalAll()唤醒所有等待线程唤醒该条件的所有线程awaitNanos(long)限时等待超时自动唤醒