深入理解独占锁ReentrantLock类锁

 ReentrantLock介绍

【1】ReentrantLock是一种基于AQS框架的应用实现,是JDK中的一种线程并发访问的同步手段,它的功能类似于synchronized是一种互斥锁,可以保证线程安全。

【2】相对于 synchronized, ReentrantLock具备如下特点:

1)可中断  2)可以设置超时时间 3)可以设置为公平锁  4)支持多个条件变量 5)与 synchronized 一样,都支持可重入

 

ReentrantLock问题分析

【1】ReentrantLock公平锁和非公平锁的性能谁更高?

  1)那肯定是非公平锁,但是为什么是非公平更高呢?

  2)因为涉及到了线程的park()与unpark()操作,不管是ReentrantLock还是synchronized,都在避免这些操作。

    (1)如ReentrantLock的非公平同步器在得不到锁的情况下,即将要进入之前会再加一次锁,生成节点之后又会加一次锁,把节点放入队列之后又会加一次锁,最终迫不得已才会进行park()操作。

    (2)如synchronized,在生成monitor的过程之中也会多次尝试加锁,避免monitor的生成。

  3)为什么要避免呢?这就涉及到线程的概念了。

    (1)因为park()与unpark()操作涉及到了线程的上下文切换,同时又涉及到了时间片轮转机制

    (2)线程上下文切换,需要将旧的线程资源保存回内存【保存执行到了哪一步,需要什么东西】,将新的线程的资源加载入CPU,让新线程具备执行的资源并开始执行。但是这些操作都是需要花费时间的,会消耗一部分时间片的资源。如(这里仅仅只是举例说明),一个时间片本来就是50s,你拿到的时候花费了一定的时间(如10s)进行上下文切换,现在刚执行不到5s,你又要进行一次切换(又要花费10s)。那下一个拿到时间片的线程会不会还是会继续切换呢?而且你要下次运行就又要等时间片了。

    (3)所以说,本质上非公平机制是为了让持有CPU的线程尽可能多的做有用的任务,减少上线下文切换带来的开销,毕竟时间片来之不易,本身就是从众多线程之中好不容易分配得来的。

 

ReentrantLock的使用

【1】使用模板

Lock lock = new ReentrantLock(); //加锁 lock.lock(); try {     // 临界区代码     // TODO 业务逻辑:读写操作不能保证线程安全 } finally {     // 解锁,放置在这里的原因是保证异常情况都不能干扰到解锁逻辑     lock.unlock(); }

 

【2】可重入的尝试

public class ReentrantLockDemo {     public 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();         }     } }

 

【3】中断机制尝试

  进行说明:这里面其实是main线程先获得了锁,所以t1线程其实是先进入队列里面,然后在main线程里面将t1设置为了中断。当main线程释放锁的时候,t1去加锁,发现自己被中断了,所以抛出中断异常,退出加锁。【其实这个中断加锁,怎么说,就是可以让失去加锁的权利,但是不影响你去排队】

@Slf4j public class ReentrantLockDemo {      public static void main(String[] args) throws InterruptedException {         ReentrantLock lock = new ReentrantLock();          Thread t1 = new Thread(() -> {              log.debug("t1启动...");              try {                 lock.lockInterruptibly();                 try {                     log.debug("t1获得了锁");                 } finally {                     lock.unlock();                 }             } catch (InterruptedException e) {                 e.printStackTrace();                 log.debug("t1等锁的过程中被中断");             }          }, "t1");                  lock.lock();         try {             log.debug("main线程获得了锁");             t1.start();             //先让线程t1执行             Thread.sleep(10000);              t1.interrupt();             log.debug("线程t1执行中断");         } finally {             lock.unlock();         }      }  }

 

【4】锁超时尝试

@Slf4j public class ReentrantLockDemo {      public static void main(String[] args) throws InterruptedException {         ReentrantLock lock = new ReentrantLock();          Thread t1 = new Thread(() -> {              log.debug("t1启动...");             //超时             try {                 // 注意: 即使是设置的公平锁,此方法也会立即返回获取锁成功或失败,公平策略不生效                 if (!lock.tryLock(1, TimeUnit.SECONDS)) {                     log.debug("等待 1s 后获取锁失败,返回");                     return;                 }             } catch (InterruptedException e) {                 e.printStackTrace();                 return;             }              try {                 log.debug("t1获得了锁");             } finally {                 lock.unlock();             }          }, "t1");           lock.lock();         try {             log.debug("main线程获得了锁");             t1.start();             //先让线程t1执行             Thread.sleep(2000);         } finally {             lock.unlock();         }      }  }

 

【5】条件变量的尝试

  说明:

    1)java.util.concurrent类库中提供Condition类来实现线程之间的协调。调用Condition.await() 方法使线程等待,其他线程调用Condition.signal() 或 Condition.signalAll() 方法唤醒等待的线程。

    2)由于可控的原因我们甚至可以多个条件队列来进行对线程调控。

  注意:调用Condition的await()和signal()方法,都必须在lock保护之内

@Slf4j public class ReentrantLockDemo {     private static ReentrantLock lock = new ReentrantLock();     private static Condition cigCon = lock.newCondition();     private static Condition takeCon = lock.newCondition();      private static boolean hashcig = false;     private static boolean hastakeout = false;      //送烟     public void cigratee(){         lock.lock();         try {             while(!hashcig){                 try {                     log.debug("没有烟,歇一会");                     cigCon.await();                  }catch (Exception e){                     e.printStackTrace();                 }             }             log.debug("有烟了,干活");         }finally {             lock.unlock();         }     }      //送外卖     public void takeout(){         lock.lock();         try {             while(!hastakeout){                 try {                     log.debug("没有饭,歇一会");                     takeCon.await();                  }catch (Exception e){                     e.printStackTrace();                 }             }             log.debug("有饭了,干活");         }finally {             lock.unlock();         }     }      public static void main(String[] args) {         ReentrantLockDemo6 test = new ReentrantLockDemo6();         new Thread(() ->{             test.cigratee();         }).start();          new Thread(() -> {             test.takeout();         }).start();          new Thread(() ->{             lock.lock();             try {                 hashcig = true;                 log.debug("唤醒送烟的等待线程");                 cigCon.signal();             }finally {                 lock.unlock();             }           },"t1").start();          new Thread(() ->{             lock.lock();             try {                 hastakeout = true;                 log.debug("唤醒送饭的等待线程");                 takeCon.signal();             }finally {                 lock.unlock();             }           },"t2").start();     }  }

 

 

ReentrantLock源码分析(版本为jdk14)

【0】前置部分最好有关于JDK实现管程的了解【可查看  深入理解AQS--jdk层面管程实现

【1】ReentrantLock类自身部分

  0)继承关系

//锁的接口定义,定义了一个锁该具备哪一些功能 public interface Lock {      void lock();      void lockInterruptibly() throws InterruptedException;      boolean tryLock();      boolean tryLock(long time, TimeUnit unit) throws InterruptedException;      void unlock();      Condition newCondition(); }

  1)属性值

//同步器句柄,同步器提供了所有的实现机制 private final Sync sync;

  2)构造方法

//默认是采用非公平的同步器 public ReentrantLock() {     sync = new NonfairSync(); }  //此外可以根据传入的参数选择同步器 public ReentrantLock(boolean fair) {     sync = fair ? new FairSync() : new NonfairSync(); }

  3)其他方法【看完之后,你会发现,其实ReentrantLock什么事都不干,统统都交给了持有的AQS同步器去干活了,有一种修饰器设计模式的味道,只是包装了一下,具体内部的同步器类型由自己选择,所以同步器显得就很重要

public class ReentrantLock implements Lock, java.io.Serializable {     .....     //获取锁定     public void lock() {         sync.lock();     }      //中断式的加锁,如果没有被中断就会加锁     public void lockInterruptibly() throws InterruptedException {         sync.lockInterruptibly();     }      //仅当在调用时锁不被另一个线程持有时才获取锁     public boolean tryLock() {         return sync.tryLock();     }    //超时加锁,限定加锁时间     public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {         return sync.tryLockNanos(unit.toNanos(timeout));     }      //尝试释放此锁     public void unlock() {         sync.release(1);     }      //返回一个用于此锁定实例的条件实例,说白了就是监视器     public Condition newCondition() {         return sync.newCondition();     }      //查询当前线程在此锁上保留的数量     public int getHoldCount() {         return sync.getHoldCount();     }      public boolean isHeldByCurrentThread() {         return sync.isHeldExclusively();     }      //判断同步器是否在被持有状态,也就是被加锁了     public boolean isLocked() {         return sync.isLocked();     }      //判断同步器的类型     public final boolean isFair() {         return sync instanceof FairSync;     }      protected Thread getOwner() {         return sync.getOwner();     }      public final boolean hasQueuedThreads() {         return sync.hasQueuedThreads();     }      //判断同步器里面是否有该线程     public final boolean hasQueuedThread(Thread thread) {         return sync.isQueued(thread);     }      public final int getQueueLength() {         return sync.getQueueLength();     }      protected Collection<Thread> getQueuedThreads() {         return sync.getQueuedThreads();     }      public boolean hasWaiters(Condition condition) {         if (condition == null)             throw new NullPointerException();         if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))             throw new IllegalArgumentException("not owner");         return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);     }      //返回条件队列里面等待的线程的个数     public int getWaitQueueLength(Condition condition) {         if (condition == null)             throw new NullPointerException();         if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))             throw new IllegalArgumentException("not owner");         return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);     }      //返回条件队列里面等待的线程     protected Collection<Thread> getWaitingThreads(Condition condition) {         if (condition == null)             throw new NullPointerException();         if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))             throw new IllegalArgumentException("not owner");         return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);     }   }

 

【2】抽象的Sync类部分

abstract static class Sync extends AbstractQueuedSynchronizer {     //定义了核心的加锁逻辑     @ReservedStackAccess     final boolean tryLock() {         Thread current = Thread.currentThread();         //获取State属性值,这是在AQS里面定义的值,用于标记是否可以加锁,0代表没有人在用锁,1代表有人在占用,大于1说明这个锁被这个人加了多次【即重入锁概念】         int c = getState();         if (c == 0) {             //CAS保证只有一个人能成功             if (compareAndSetState(0, 1)) {                 //设置持有锁的线程                 setExclusiveOwnerThread(current);                 return true;             }         } else if (getExclusiveOwnerThread() == current) { //走到这里说明有人持有了锁,但是可以判断持有的人是不是自己【可重入】             if (++c < 0) // overflow                 throw new Error("Maximum lock count exceeded");             //因为每一次重入都会导致State的值+1,所以解锁的时候对应要减1             setState(c);             return true;         }         return false;     }      //为子类留下的加锁逻辑的抽象方法     abstract boolean initialTryLock();      //核心加锁逻辑里面便是使用抽象方法进行加锁     @ReservedStackAccess     final void lock() {         if (!initialTryLock())             acquire(1);     }      @ReservedStackAccess     final void lockInterruptibly() throws InterruptedException {         if (Thread.interrupted())             throw new InterruptedException();         if (!initialTryLock())             acquireInterruptibly(1);     }      @ReservedStackAccess     final boolean tryLockNanos(long nanos) throws InterruptedException {         if (Thread.interrupted())             throw new InterruptedException();         return initialTryLock() || tryAcquireNanos(1, nanos);     }      //尝试释放锁     @ReservedStackAccess     protected final boolean tryRelease(int releases) {         int c = getState() - releases;         if (getExclusiveOwnerThread() != Thread.currentThread())             throw new IllegalMonitorStateException();         boolean free = (c == 0);         if (free)             setExclusiveOwnerThread(null);         setState(c);         return free;     }      protected final boolean isHeldExclusively() {         // While we must in general read state before owner,         // we don't need to do so to check if current thread is owner         return getExclusiveOwnerThread() == Thread.currentThread();     }      final ConditionObject newCondition() {         return new ConditionObject();     }      // Methods relayed from outer class      final Thread getOwner() {         return getState() == 0 ? null : getExclusiveOwnerThread();     }      final int getHoldCount() {         return isHeldExclusively() ? getState() : 0;     }      final boolean isLocked() {         return getState() != 0;     }      /**      * Reconstitutes the instance from a stream (that is, deserializes it).      */     private void readObject(java.io.ObjectInputStream s)         throws java.io.IOException, ClassNotFoundException {         s.defaultReadObject();         setState(0); // reset to unlocked state     } }

 

【3】实现抽象的 Sync类 的公平锁 FairSync类部分

static final class FairSync extends Sync {     //尝试锁定方法     final boolean initialTryLock() {         Thread current = Thread.currentThread();         int c = getState();         if (c == 0) {             //看得出来首先队列要为空,其次才是CAS加锁成功,才算能够持有锁              //也就是说队列不为空,连CAS加锁的资格都没有,所以十分公平             if (!hasQueuedThreads() && compareAndSetState(0, 1)) {                 setExclusiveOwnerThread(current);                 return true;             }         } else if (getExclusiveOwnerThread() == current) {             if (++c < 0) // overflow                 throw new Error("Maximum lock count exceeded");             setState(c);             return true;         }         return false;     }      //尝试获取方法     protected final boolean tryAcquire(int acquires) {         if (getState() == 0 && !hasQueuedPredecessors() &&             compareAndSetState(0, acquires)) {             setExclusiveOwnerThread(Thread.currentThread());             return true;         }         return false;     } }  //AbstractQueuedSynchronizer类#hasQueuedThreads方法 //判断队列是否为空【由于AQS里面采用的是链表实现队列效果,所以是判断节点情况】 public final boolean hasQueuedThreads() {     for (Node p = tail, h = head; p != h && p != null; p = p.prev)         if (p.status >= 0)             return true;     return false; }

 

【4】实现抽象的 Sync类 的非公平锁 NonfairSync类部分

//与公平的同步器进行比较的话,会发现,他们本质没什么区别,因为大多数走的都是抽象方法的逻辑和AQS的方法 //最大的区别在于加锁的方式不同,公平方式,队列没人才去加锁;非公平方式,不管队列有没有人,都是直接去加锁,加到了就持有 static final class NonfairSync extends Sync {     //尝试锁定方法     final boolean initialTryLock() {         Thread current = Thread.currentThread();         //直接尝试CAS获取加锁权利         if (compareAndSetState(0, 1)) { // first attempt is unguarded             setExclusiveOwnerThread(current);             return true;         } else if (getExclusiveOwnerThread() == current) {             int c = getState() + 1;             if (c < 0) // overflow                 throw new Error("Maximum lock count exceeded");             setState(c);             return true;         } else             return false;     }      //尝试获取方法     protected final boolean tryAcquire(int acquires) {         //判断是否有人持有锁,没有则去加锁         if (getState() == 0 && compareAndSetState(0, acquires)) {             setExclusiveOwnerThread(Thread.currentThread());             return true;         }         return false;     } }

 

发表评论

评论已关闭。

相关文章