文档章节

Java锁之ReentrantLock(一)

木木匠
 木木匠
发布于 2018/08/10 22:42
字数 1553
阅读 379
收藏 7

一、ReenTrantLock结构

 

图1-1

根据上图可以知道,ReenTrantLock继承了Lock接口,Lock接口声明方法如下:

方法名 说明 抛出异常
lock() 一直阻塞获取锁,直到获取成功
lockInterruptibly() 尝试获取锁,直到获取锁或者线程被中断 InterruptedException
tryLock() 尝试获取空闲的锁,获取成功返回true,获取失败返回false,不会阻塞,立即返回
tryLock(long time, TimeUnit unit) 尝试在time时间内获取空闲的锁,在等待时间内可以被中断 InterruptedException
unlock() 释放锁
newCondition() 返回当前锁的一个condition实例,用于条件性等待

二、Lock的实现类ReentrantLock

1.ReentrantLock的部分方法

 

 

图2-1

  • 根据图2-1可以知道 Sync对象提供了所有的实现机制,而Sync继承了AbstractQueuedSynchronizer
  • NonfairSync 不公平锁,继承了Sync
  • FairSync 公平同步,继承了Sync

1. Sync

Sync是NonfairSync 和FairSync 的父类,声明方法如下:

      /**
       * 抽象方法,获取锁
       */
      abstract void lock();

      /**
       * 实现非公平锁获取逻辑
       */
      final boolean  nonfairTryAcquire(int acquires) {
          final Thread current = Thread.currentThread();
          int c = getState(); //父类同步器方法,获取当前同步状态,后续文章会分析
          if (c == 0) {//状态等于0表示没有获取到锁
              if (compareAndSetState(0, acquires)) { //CAS方式修改状态
                  setExclusiveOwnerThread(current); //修改成功后设置当前线程为锁的所有者
                  return true;
              }
          }
          else if (current == getExclusiveOwnerThread()) {//当前锁已被占用,判断是不是自己获取到了锁,锁重入
              int nextc = c + acquires; //获取锁的计数器
              if (nextc < 0) // overflow //因为是int类型,如果超过int最大值会溢出为负
                  throw new Error("Maximum lock count exceeded");
              setState(nextc);//设置计数器为状态值
              return true;
          }
          return false;
      }

      protected final boolean tryRelease(int releases) {
          int c = getState() - releases;//释放锁,同步状态减int值
          if (Thread.currentThread() != getExclusiveOwnerThread())
              throw new IllegalMonitorStateException(); //如果当前相差不是锁的拥有者,抛出异常
          boolean free = false;
          if (c == 0) { //如果同步状态值为0,表示锁已经释放成功
              free = true; 
              setExclusiveOwnerThread(null); // 设置锁的拥有线程为null
          }
          setState(c);//重新赋值同步状态
          return free;
      }
      //判断当前线程是不是锁独占
      protected final boolean isHeldExclusively() {
        
          return getExclusiveOwnerThread() == Thread.currentThread();
      }
      //返回锁的ConditionObject实例
      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; //可以知道判断获取锁的关键就是是否不等于0
      }
复制代码

2. NonfairSync 非公平锁

 static final class NonfairSync extends Sync {
       private static final long serialVersionUID = 7316153563782823691L;

       /**
        * Performs lock.  Try immediate barge, backing up to normal
        * acquire on failure.
        */
       final void lock() {
           if (compareAndSetState(0, 1))//CAS获取锁
               setExclusiveOwnerThread(Thread.currentThread());
           else
               acquire(1);
       }

       protected final boolean tryAcquire(int acquires) {
           return nonfairTryAcquire(acquires);
       }
   }
复制代码
  • 根据源码发现 非公平锁继承了Sync父类,由于锁的释放不存在公平与不公平,所以公平锁和非公平锁只实现各自获取锁的逻辑。根据非公平锁的源码发现,其内部只实现了lock()tryAcquire(int acquires)方法,其中和tryAcquire(int acquires)方法直接调用了父类的nonfairTryAcquire(acquires),介绍父类的时候已经解析过,不清楚可以看上文Sync解析部分。根据lock源码发现,开始判断是否是第一次获取锁,如果获取锁成功,就把当前线程设置为锁的占有者,否则调用父类的acquire(1)方法(下一篇介绍同步器会介绍)。

3. FairSync 公平锁

 static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
        /**调用父类的acquire()方法*/
        final void lock() {
            acquire(1);
        }

        /**
         * 尝试获取锁
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
复制代码
  • 根据上面代码发现公平锁的获取代码和非公平锁获取锁代码很相识,公平锁只多了一个 !hasQueuedPredecessors()判断,其实该方法就是判断是否有比当前线程等待最长时间的线程,如果没有,那么就尝试获取锁,获取成功后设置当前线程为锁的占有者,所以,公平与不公平就是是否按照时间等待来获取锁的,比如食堂吃饭,排队一个个来,这就是公平,如果有人插队,这就是不公平。

3. ReentrantLock 其他方法

 public ReentrantLock() {
        sync = new NonfairSync();
    }
public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
  public void lock() {
        sync.lock();
    }
 public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }
    //指定超时时间内获取锁,阻塞时间为timeout
 public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
 public Condition newCondition() {
        return sync.newCondition();
    }
public int getHoldCount() {
        return sync.getHoldCount();
    }
  public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();
    }
 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);
    }

复制代码
  • 其实根据上面源码发现,不管是实现LOCK接口的方法,还是后续新增的方法,其实现功能都依托于一个对象,那就是sync,在介绍sync时候已经说过,它就是继承了AbstractQueuedSynchronizer同步器,很多方法都是直接调用父类同步器的方法,下一篇《java锁之ReentrantLock(二)》会重点解析AbstractQueuedSynchronizer同步器源码,分析同步器是如何依托于FIFO队列完成锁的机制。

三、 总结

  • ReentrantLock实现了LOCK接口
  • ReentrantLock可以实现公平锁和非公平锁获取
  • ReentrantLock可以进行超时时间内获取锁
  • ReentrantLock可以进行条件等待和唤醒
  • ReentrantLock可以获取锁响应中断

© 著作权归作者所有

木木匠
粉丝 105
博文 30
码字总数 65486
作品 0
广州
高级程序员
私信 提问
Java核心(三)并发中的线程同步与锁

乐观锁、悲观锁、公平锁、自旋锁、偏向锁、轻量级锁、重量级锁、锁膨胀...难理解?不存的!来,话不多说,带你飙车。 上一篇介绍了线程池的使用,在享受线程池带给我们的性能优势之外,似乎也...

王磊的博客
2018/11/22
96
0
synchronized ReentrantLock 比较分析

     在编写多线程代码的时候,对于不允许并发的代码,很多需要加锁进行处理。在进行加锁处理时候,synchronized作为java的内置锁,同时也是java关键字,最为被人熟知,即使是最初级的j...

阿姆斯特朗回旋炮
2018/07/18
0
0
死磕 java同步系列之ReentrantLock VS synchronized——结果可能跟你想的不一样

问题 (1)ReentrantLock有哪些优点? (2)ReentrantLock有哪些缺点? (3)ReentrantLock是否可以完全替代synchronized? 简介 synchronized是Java原生提供的用于在多线程环境中保证同步的...

彤哥读源码
06/11
1K
6
4种常用Java线程锁的特点,性能比较及使用场景

多个线程同时对同一个对象进行读写操作,很容易会出现一些难以预料的问题。所以很多时候我们需要给代码块加锁,同一时刻只允许一个线程对某个对象进行操作。多线程之所以会容易引发一些难以发现...

mikechen优知
03/10
125
0
JDK基础--Java中的锁概念

Java中的锁概念 掌握Java中锁是Java多线程编程中绕不开的知识,只有知道理解Java各种锁才能在编码过程中灵活运用,写出更高效的多线程程序。而理解掌握锁的第一步,可从宏观上对比理解一下各...

spinachgit
04/16
23
0

没有更多内容

加载失败,请刷新页面

加载更多

64.监控平台介绍 安装zabbix 忘记admin密码

19.1 Linux监控平台介绍 19.2 zabbix监控介绍 19.3/19.4/19.6 安装zabbix 19.5 忘记Admin密码如何做 19.1 Linux监控平台介绍: 常见开源监控软件 ~1.cacti、nagios、zabbix、smokeping、ope...

oschina130111
今天
13
0
当餐饮遇上大数据,嗯真香!

之前去开了一场会,主题是「餐饮领袖新零售峰会」。认真听完了餐饮前辈和新秀们的分享,觉得获益匪浅,把脑子里的核心纪要整理了一下,今天和大家做一个简单的分享,欢迎感兴趣的小伙伴一起交...

数澜科技
今天
7
0
DNS-over-HTTPS 的下一代是 DNS ON BLOCKCHAIN

本文作者:PETER LAI ,是 Diode 的区块链工程师。在进入软件开发领域之前,他主要是在做工商管理相关工作。Peter Lai 也是一位活跃的开源贡献者。目前,他正在与 Diode 团队一起开发基于区块...

红薯
今天
13
0
CC攻击带来的危害我们该如何防御?

随着网络的发展带给我们很多的便利,但是同时也带给我们一些网站安全问题,网络攻击就是常见的网站安全问题。其中作为站长最常见的就是CC攻击,CC攻击是网络攻击方式的一种,是一种比较常见的...

云漫网络Ruan
今天
12
0
实验分析性专业硕士提纲撰写要点

为什么您需要研究论文的提纲? 首先当您进行研究时,您需要聚集许多信息和想法,研究论文提纲可以较好地组织你的想法, 了解您研究资料的流畅度和程度。确保你写作时不会错过任何重要资料以此...

论文辅导员
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部