深入理解独占锁ReentrantLock类锁(独占锁是指任何时候都只有一个线程能执行资源操作)

  本篇文章为你整理了深入理解独占锁ReentrantLock类锁(独占锁是指任何时候都只有一个线程能执行资源操作)的详细内容,包含有独占锁是指任何时候 独占锁是指任何时候都只有一个线程能执行资源操作 独占锁和可重入锁 独占方式锁定此配置文件 深入理解独占锁ReentrantLock类锁,希望能帮助你了解 深入理解独占锁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");

  
说明:

   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();

  
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);

  


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 dont 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;

  }

 

  

  以上就是深入理解独占锁ReentrantLock类锁(独占锁是指任何时候都只有一个线程能执行资源操作)的详细内容,想要了解更多 深入理解独占锁ReentrantLock类锁的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: