ReentrantLock 一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。 这个类主要基于AQS(AbstractOwnableSynchronizer)封装的 公平与非公平锁。

所谓公平锁就是指 在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程,换句话说也就是先被锁定的线程首先获得锁。 非公平锁正好相反,解锁时没有固定顺序。

让我们边分析源代码边学习如何使用该类

先来看一下构造参数,默认是非公平锁。

  1. /**
  2. * Creates an instance of {@code ReentrantLock}.
  3. * This is equivalent to using {@code ReentrantLock(false)}.
  4. */
  5. public ReentrantLock() {
  6. sync = new NonfairSync();
  7. }
  8.  
  9. /**
  10. * Creates an instance of {@code ReentrantLock} with the
  11. * given fairness policy.
  12. *
  13. * @param fair {@code true} if this lock should use a fair ordering policy
  14. */
  15. public ReentrantLock(boolean fair) {
  16. sync = fair ? new FairSync() : new NonfairSync();
  17. }

NonfairSync是非公平锁,我们先来看非公平锁,是一个内部类继承了Sync。

  1. /**
  2. * Sync object for non-fair locks
  3. */
  4. static final class NonfairSync extends Sync {
  5. private static final long serialVersionUID = 7316153563782823691L;
  6.  
  7. /**
  8. * Performs lock. Try immediate barge, backing up to normal
  9. * acquire on failure.
  10. */
  11. final void lock() {
  12. if (compareAndSetState(0, 1))
  13. setExclusiveOwnerThread(Thread.currentThread());
  14. else
  15. acquire(1);
  16. }
  17.  
  18. protected final boolean tryAcquire(int acquires) {
  19. return nonfairTryAcquire(acquires);
  20. }
  21. }

我们看到它继承了Sync,我们接着看这个类的源码。

  1. abstract static class Sync extends AbstractQueuedSynchronizer {
  2. private static final long serialVersionUID = -5179523762034025860L;
  3.  
  4. abstract void lock();
  5.  
  6. final boolean nonfairTryAcquire(int acquires) {
  7. final Thread current = Thread.currentThread();
  8. int c = getState();
  9. if (c == 0) {
  10. if (compareAndSetState(0, acquires)) {
  11. setExclusiveOwnerThread(current);
  12. return true;
  13. }
  14. }
  15. else if (current == getExclusiveOwnerThread()) {
  16. int nextc = c + acquires;
  17. if (nextc < 0) // overflow
  18. throw new Error("Maximum lock count exceeded");
  19. setState(nextc);
  20. return true;
  21. }
  22. return false;
  23. }
  24.  
  25. protected final boolean tryRelease(int releases) {
  26. int c = getState() - releases;
  27. if (Thread.currentThread() != getExclusiveOwnerThread())
  28. throw new IllegalMonitorStateException();
  29. boolean free = false;
  30. if (c == 0) {
  31. free = true;
  32. setExclusiveOwnerThread(null);
  33. }
  34. setState(c);
  35. return free;
  36. }
  37. }

Sync这个类与AbstractQueuedSynchronizer 一起完成了锁的逻辑,现在我们开始从头分析一个线程如何获取锁,以及获取不到锁时如何被阻塞。当用户调用lock方法获取锁的时候,首先会先通过compareAndSetState(NonfairSync第11行)来设置锁定状态,如果原先状态为0,则说明目前没有线程持有锁,那么设置状态为1,并且设置当前线程是当前拥有独占访问的线程(setExclusiveOwnerThread),那么另外一种情况就是compareAndSetState方法返回false,也就是说之前已经有线程持有锁,那么就会执行acquire方法(NonfairSync第15行),这个方法是AbstractQueuedSynchronizer里面的方法。

  1. public final void acquire(int arg) {
  2. if (!tryAcquire(arg) &&
  3. acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
  4. selfInterrupt();
  5. }

首先调用了tryAcquire方法,这个类在子类当中实现,也就是上面NonfairSync第18行,实际上他调用了 nonfairTryAcquire,这个方法分两步,首先在判断一下状态(state)是否等于0,也就是重新尝试获取所,如果获取到锁则改变状态compareAndSetState 然后设置当前线程是当前拥有独占访问的线程(setExclusiveOwnerThread),跟上面讲到的一样。

如果重新尝试获取所失败,则判断是不是当前线程重复加锁,如果是的话就把状态进行增加。

如果上面都不是就返回FALSE, 如果返回FALSE 那么 acquire(int arg)方法的acquireQueued就会执行,这个方法会把不能获取锁的线程形成一个CHL队列保存起来,然后把线程阻塞。上面就基本讲完了

线程如何获取锁, 获取到锁就把状态设置成1。

如果是持有锁的线程继续调用 LOCK方法,那就把状态进行叠加。

如果获取不到锁,那么AbstractQueuedSynchronizer 会把线程以一个CHL队列的形式保存起来,然后设置线程阻塞,等待释放。

然后就是释放锁的操作

  1. public void unlock() {
  2. sync.release(1);
  3. }

这个release方法又是AbstractQueuedSynchronizer 里面提供的方法。

  1. public final boolean release(int arg) {
  2. if (tryRelease(arg)) {
  3. Node h = head;
  4. if (h != null && h.waitStatus != 0)
  5. unparkSuccessor(h);
  6. return true;
  7. }
  8. return false;
  9. }

首先在第2行先调用tryRelease尝试释放锁,这个方法是在ReentrantLock的内部类Sync当中重写的,释放成功会返回TRUE。然后调用unparkSuccessor来释放阻塞队列当中的线程,然后被唤醒的线程会继续获取锁,如此反复。

最后我们再来看一下说明是公平锁FairSync。

  1. static final class FairSync extends Sync {
  2. private static final long serialVersionUID = -3000897897090466540L;
  3.  
  4. final void lock() {
  5. acquire(1);
  6. }
  7.  
  8. /**
  9. * Fair version of tryAcquire. Don't grant access unless
  10. * recursive call or no waiters or is first.
  11. */
  12. protected final boolean tryAcquire(int acquires) {
  13. final Thread current = Thread.currentThread();
  14. int c = getState();
  15. if (c == 0) {
  16. if (!hasQueuedPredecessors() &&
  17. compareAndSetState(0, acquires)) {
  18. setExclusiveOwnerThread(current);
  19. return true;
  20. }
  21. }
  22. else if (current == getExclusiveOwnerThread()) {
  23. int nextc = c + acquires;
  24. if (nextc < 0)
  25. throw new Error("Maximum lock count exceeded");
  26. setState(nextc);
  27. return true;
  28. }
  29. return false;
  30. }
  31. }

仔细观察你会发现它与非公平锁唯一的区别就是在tryAcquire这里方法里面。上面已经用红色标记上了,主要是多了这么一个条件,就体现了锁的公平性。

当一个线程尝试获取锁时,那么先会判断当前有没有已经等待获取锁的线程队列,如果的话,按照公平原则,那么当前线程就会被加入阻塞队列的尾巴,如果是非公平锁,那么则不会判断。

这个类的核心原理基本介绍完了,其实主要核心的东西是在AbstractQueuedSynchronizer这个类里。

这里给大家提供一个关于AbstractQueuedSynchronizer的源码分析的文章:

http://ifeve.com/introduce-abstractqueuedsynchronizer/ 大家可以结合着一起看一下。

Java并发编程 ReentrantLock 源码分析的更多相关文章

  1. Java并发编程-ReentrantLock源码分析

    一.前言 在分析了 AbstractQueuedSynchronier 源码后,接着分析ReentrantLock源码,其实在 AbstractQueuedSynchronizer 的分析中,已经提到 ...

  2. Java并发编程-AbstractQueuedSynchronizer源码分析

    简介 提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架.该同步器(以下简称同步器)利用了一个int来表示状态,期望它能够成为实现大部分同步需求的基础.使用的方法是继承,子类通过 ...

  3. Java并发编程 LockSupport源码分析

    这个类比较简单,是一个静态类,不需要实例化直接使用,底层是通过java未开源的Unsafe直接调用底层操作系统来完成对线程的阻塞. package java.util.concurrent.locks ...

  4. java 并发编程——Thread 源码重新学习

    Java 并发编程系列文章 Java 并发基础——线程安全性 Java 并发编程——Callable+Future+FutureTask java 并发编程——Thread 源码重新学习 java并发 ...

  5. Java异步编程——深入源码分析FutureTask

    Java的异步编程是一项非常常用的多线程技术. 之前通过源码详细分析了ThreadPoolExecutor<你真的懂ThreadPoolExecutor线程池技术吗?看了源码你会有全新的认识&g ...

  6. Java并发-ConcurrentModificationException原因源码分析与解决办法

    一.异常原因与异常源码分析 对集合(List.Set.Map)迭代时对其进行修改就会出现java.util.ConcurrentModificationException异常.这里以ArrayList ...

  7. 并发编程—— FutureTask 源码分析

    1. 前言 当我们在 Java 中使用异步编程的时候,大部分时候,我们都会使用 Future,并且使用线程池的 submit 方法提交一个 Callable 对象.然后调用 Future 的 get ...

  8. 并发编程 —— Timer 源码分析

    前言 在平时的开发中,肯定需要使用定时任务,而 Java 1.3 版本提供了一个 java.util.Timer 定时任务类.今天一起来看看这个类. 1.API 介绍 Timer 相关的有 3 个类: ...

  9. Java并发编程之ReentrantLock源码分析

    ReentrantLock介绍 从JDK1.5之前,我们都是使用synchronized关键字来对代码块加锁,在JDK1.5引入了ReentrantLock锁.synchronized关键字性能比Re ...

随机推荐

  1. 构建你的长寿命的API第1部分:规范驱动的API开发

    构建你的长寿命的API第1部分:规范驱动的API开发 这篇文章是由MuleSoft的Mike Stowe在nginx.conf 2016公布的演示文稿改编的.第一部分重点是规范驱动的API开发. 第二 ...

  2. 怎样为你的CSDN博客增加百度统计

    曾经CSDN使用的 量子统计 能够非常好的统计我们的博客的訪问数量.地域等等信息,可是不知道后来为什么不在使用了.那么怎样找到 一种替换的方式那? 下边,就给大家介绍一下怎样使用百度统计. 百度统计账 ...

  3. JQ:命令行 json 解析神器 —— 命令行的Jsonview

  4. chrome 版本 29.0.1547.76 m 解决打开新标签页后的恶心页面的问题

    个人非常不喜欢这个版本的新标签页的样子,特别是一再输入框中输入要搜索的东西,自动跑到标题栏中去了,比吃屎还恶心.下面是解决办法: 在地址栏输入:chrome://flags/ 按Ctrl+F,输入下面 ...

  5. django session入门详解

    概括性的讲: 1.django默认是打开对session的支持的 2.默认情况下session相关的数据会保存在数据库中.浏览器端只保存了session id session 的科普: 1.动态网站中 ...

  6. [na]计算机网络性能指标(延迟/吞吐量/RTT等)

    参考 计算机网络性能指标 计算机网络性能指标 带宽.速率.延迟.吞吐量.丢包率(无线验收标准一般-75dbm,del<100ms,丢包率3%) 带宽x延迟 决定着路上的数据的多少. 速率: 连接 ...

  7. 【Unity】12.3 Off Mesh Link组件

    开发环境:Win10.Unity5.3.4.C#.VS2015 创建日期:2016-05-09 一.简介 Off Mesh Link组件用于手动指定路线来生成分离的网格连接.例如,游戏中让行进对象上下 ...

  8. CSS中Zen Coding

    值别名 有几个常用的别名: p → % e → em x → ex 可以用这些别名来代替完整的单位: w100p → width: 100% m10p30e5x → margin: 10% 30em ...

  9. cocos2dx-lua class解析

    function class(classname, super) local superType = type(super) local cls --如果父类既不是函数也不是table则说明父类为空 ...

  10. iOS按钮的基本使用代码优化

    将图片按钮进行连线, 声明方法同时连接六个按钮 -(void)move:(UIButton *)btn{ //    NSLog(@"看见一个美女"); //头尾式动画 //0.开 ...