大家都用过ReentrantLock,但是大家对内部实现是否足够了解呢,下面我就简单说一下其中的实现原理。

  ReentrantLock是可重入锁,也就是同一个线程可以多次获取锁,每获取一次就会进行一次计数,解锁的时候就会递减这个计数,直到计数变为0。

  它有两种实现,一种是公平锁,一种是非公平锁,那么默认是什么锁呢?看完如下代码想必你也知道了。  

  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. }

  它的内部结构的实现是如何的呢? 首先NonFairSync类是静态内部类,它继承了Sync。

  1. /**
  2. * Sync object for non-fair locks
  3. */
  4. static final class NonfairSync extends Sync

  Sync继承了AbstractQueuedSynchronizer,简称AQS。同时Sync里边实现了tryRelease方法,因为公平锁和非公平锁都可以用这个方法释放锁。

  1. /**
  2. * Base of synchronization control for this lock. Subclassed
  3. * into fair and nonfair versions below. Uses AQS state to
  4. * represent the number of holds on the lock.
  5. */
  6. abstract static class Sync extends AbstractQueuedSynchronizer

  继续看非公平锁的lock方法,采用CAS进行当前状态的设置state=0,表示没有线程占用,state=1表示已经有现成占用了,设置成功了,将当前线程设置为线程拥有者,并且是排他的。如果有现成占用了,那么需要进入acquire(1),需要获取一个锁。

  1. /**
  2. * Performs lock. Try immediate barge, backing up to normal
  3. * acquire on failure.
  4. */
  5. final void lock() {
  6. if (compareAndSetState(0, 1))
  7. setExclusiveOwnerThread(Thread.currentThread());
  8. else
  9. acquire(1);
  10. }

  acquire方法首先进行tryAcquire,尝试获取锁,即调用nonfairTryAcquire,判断当前锁是否state=0, 则没有现成占用,则进行设置。如果被占用了判断该线程是否是当前线程占用的,如果是的话,那么可以进行重入,即当前可以获取锁,计数器进行加1。否则的话返回失败。返回失败后执行addWaiter方法,也就是添加到等待的队列。Node是一个双向列表,也就是把需要等待的线程放到放到Node,并且链接起来。

  1. /**
  2. * Acquires in exclusive mode, ignoring interrupts. Implemented
  3. * by invoking at least once {@link #tryAcquire},
  4. * returning on success. Otherwise the thread is queued, possibly
  5. * repeatedly blocking and unblocking, invoking {@link
  6. * #tryAcquire} until success. This method can be used
  7. * to implement method {@link Lock#lock}.
  8. *
  9. * @param arg the acquire argument. This value is conveyed to
  10. * {@link #tryAcquire} but is otherwise uninterpreted and
  11. * can represent anything you like.
  12. */
  13. public final void acquire(int arg) {
  14. if (!tryAcquire(arg) &&
  15. acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
  16. selfInterrupt();
  17. }
  18.  
  19. /**
  20. * Performs non-fair tryLock. tryAcquire is implemented in
  21. * subclasses, but both need nonfair try for trylock method.
  22. */
  23. final boolean nonfairTryAcquire(int acquires) {
  24. final Thread current = Thread.currentThread();
  25. int c = getState();
  26. if (c == 0) {
  27. if (compareAndSetState(0, acquires)) {
  28. setExclusiveOwnerThread(current);
  29. return true;
  30. }
  31. }
  32. else if (current == getExclusiveOwnerThread()) {
  33. int nextc = c + acquires;
  34. if (nextc < 0) // overflow
  35. throw new Error("Maximum lock count exceeded");
  36. setState(nextc);
  37. return true;
  38. }
  39. return false;
  40. }
  41.  
  42. /**
  43. * Creates and enqueues node for current thread and given mode.
  44. *
  45. * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
  46. * @return the new node
  47. */
  48. private Node addWaiter(Node mode) {
  49. Node node = new Node(Thread.currentThread(), mode);
  50. // Try the fast path of enq; backup to full enq on failure
  51. Node pred = tail;
  52. if (pred != null) {
  53. node.prev = pred;
  54. if (compareAndSetTail(pred, node)) {
  55. pred.next = node;
  56. return node;
  57. }
  58. }
  59. enq(node);
  60. return node;
  61. }

  接下来看一下Node的大致内容,有两个指针,一个是prev,一个是next,还保存着当前的线程。同时里边还有一个共享锁和独占锁,SHARED和EXCLUSIVE。ReentrantLock采用的就是独占锁。Semaphore,CountDownLatch等采用的是共享锁,即有多个线程可以同时获取锁。

  1. static final class Node {
  2. /** Marker to indicate a node is waiting in shared mode */
  3. static final Node SHARED = new Node();
  4. /** Marker to indicate a node is waiting in exclusive mode */
  5. static final Node EXCLUSIVE = null;
  6. /**
  7. * Link to predecessor node that current node/thread relies on
  8. * for checking waitStatus. Assigned during enqueuing, and nulled
  9. * out (for sake of GC) only upon dequeuing. Also, upon
  10. * cancellation of a predecessor, we short-circuit while
  11. * finding a non-cancelled one, which will always exist
  12. * because the head node is never cancelled: A node becomes
  13. * head only as a result of successful acquire. A
  14. * cancelled thread never succeeds in acquiring, and a thread only
  15. * cancels itself, not any other node.
  16. */
  17. volatile Node prev;
  18.  
  19. /**
  20. * Link to the successor node that the current node/thread
  21. * unparks upon release. Assigned during enqueuing, adjusted
  22. * when bypassing cancelled predecessors, and nulled out (for
  23. * sake of GC) when dequeued. The enq operation does not
  24. * assign next field of a predecessor until after attachment,
  25. * so seeing a null next field does not necessarily mean that
  26. * node is at end of queue. However, if a next field appears
  27. * to be null, we can scan prev's from the tail to
  28. * double-check. The next field of cancelled nodes is set to
  29. * point to the node itself instead of null, to make life
  30. * easier for isOnSyncQueue.
  31. */
  32. volatile Node next;
  33.  
  34. /**
  35. * The thread that enqueued this node. Initialized on
  36. * construction and nulled out after use.
  37. */
  38. volatile Thread thread;
  39.  
  40. /**
  41. * Link to next node waiting on condition, or the special
  42. * value SHARED. Because condition queues are accessed only
  43. * when holding in exclusive mode, we just need a simple
  44. * linked queue to hold nodes while they are waiting on
  45. * conditions. They are then transferred to the queue to
  46. * re-acquire. And because conditions can only be exclusive,
  47. * we save a field by using special value to indicate shared
  48. * mode.
  49. */
  50. Node nextWaiter;

  大致的思路我们看了一下,总体的流程图我画了一下。ReentrankLock内核采用的是AQS实现的,AQS里边采用的是双向链表,即如果当前线程未获取到锁将会加入到链表中。

  那么公平锁和非公平锁的实现的不同点在哪里呢?公平锁和非公平锁就差在 !hasQueuedPredecessors() ,也就是前边没有排队者的话,我就可以获取锁了。

  1. /**
  2. * Fair version of tryAcquire. Don't grant access unless
  3. * recursive call or no waiters or is first.
  4. */
  5. protected final boolean tryAcquire(int acquires) {
  6. final Thread current = Thread.currentThread();
  7. int c = getState();
  8. if (c == 0) {
  9. if (!hasQueuedPredecessors() &&
  10. 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)
  18. throw new Error("Maximum lock count exceeded");
  19. setState(nextc);
  20. return true;
  21. }
  22. return false;
  23. }

  如果当前线程之前还有线程等待就会返回true,如果当前节点是头结点,或者当前队列为空就会返回false。非公平锁没有这句话的判断,所以直接去竞争锁。

  1. /**
  2. * Queries whether any threads have been waiting to acquire longer
  3. * than the current thread.
  4. *
  5. * <p>An invocation of this method is equivalent to (but may be
  6. * more efficient than):
  7. * <pre> {@code
  8. * getFirstQueuedThread() != Thread.currentThread() &&
  9. * hasQueuedThreads()}</pre>
  10. *
  11. * <p>Note that because cancellations due to interrupts and
  12. * timeouts may occur at any time, a {@code true} return does not
  13. * guarantee that some other thread will acquire before the current
  14. * thread. Likewise, it is possible for another thread to win a
  15. * race to enqueue after this method has returned {@code false},
  16. * due to the queue being empty.
  17. *
  18. * <p>This method is designed to be used by a fair synchronizer to
  19. * avoid <a href="AbstractQueuedSynchronizer#barging">barging</a>.
  20. * Such a synchronizer's {@link #tryAcquire} method should return
  21. * {@code false}, and its {@link #tryAcquireShared} method should
  22. * return a negative value, if this method returns {@code true}
  23. * (unless this is a reentrant acquire). For example, the {@code
  24. * tryAcquire} method for a fair, reentrant, exclusive mode
  25. * synchronizer might look like this:
  26. *
  27. * <pre> {@code
  28. * protected boolean tryAcquire(int arg) {
  29. * if (isHeldExclusively()) {
  30. * // A reentrant acquire; increment hold count
  31. * return true;
  32. * } else if (hasQueuedPredecessors()) {
  33. * return false;
  34. * } else {
  35. * // try to acquire normally
  36. * }
  37. * }}</pre>
  38. *
  39. * @return {@code true} if there is a queued thread preceding the
  40. * current thread, and {@code false} if the current thread
  41. * is at the head of the queue or the queue is empty
  42. * @since 1.7
  43. */
  44. public final boolean hasQueuedPredecessors() {
  45. // The correctness of this depends on head being initialized
  46. // before tail and on head.next being accurate if the current
  47. // thread is first in queue.
  48. Node t = tail; // Read fields in reverse initialization order
  49. Node h = head;
  50. Node s;
  51. return h != t &&
  52. ((s = h.next) == null || s.thread != Thread.currentThread());
  53. }

  这个就是ReentrantLock的基本原理,接下来咱们继续看看与之一块使用的Condition。Condition是一个接口,它的实现类是ConditionObject。调用await的时候也会将当前线程的一些信息加入到队列当中。ConditionObject中有一个firstWaiter和LastWaiter分别指向的了等待队列的头和尾。

  当调用Condition的signal方法是,则会将第一个Node转换到同步队列,如下图所示。

  好了,总结一下:

  1. ReentrankLock默认是非公平锁。

  2.ReentrankLock的内部实现采用的AQS的双向链表实现。获取锁的线程会被封装成Node里边,供后续使用。

  3.公平锁采用判断当前Node是不是头结点,如果是的话就获取锁并做业务处理,不是头结点的不能获取所。

  4.非公平锁没有判断当前结点,采用CAS,谁第一个拿到了state=0,则视为获取锁。

  5.Condition的await和notify也采用类似的机制,当执行await是,会将当前线程信息的相关信息放入到Node的列表,记录firstWaiter和lastWaiter指向的信息。

  

  希望对大家有所帮助,如果有问题的请及时指出。

  

聊聊ReentrantLock的内部实现的更多相关文章

  1. 聊聊ReentrantLock实现原理

    ReentrantLock 是常用的锁,相对于Synchronized ,lock锁更人性化,阅读性更强 从LOCK切入 考虑下面的场景如果有A,B线程,同时去执行lock.lock(Lock loc ...

  2. 聊聊ReentrantLock基于AQS的公平锁和非公平锁的实现区别

    ReentrantLock锁的实现是基于AQS实现的,所以先简单说下AQS: AQS是AbstractQueuedSynchronizer缩写,顾名思义:抽象的队列同步器,它是JUC里面许多同步工具类 ...

  3. 【JUC】JDK1.8源码分析之ReentrantLock(三)

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

  4. java分析源码-ReentrantLock

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

  5. Java中可重入锁ReentrantLock原理剖析

    本文由码农网 – 吴极心原创,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划! 一. 概述 本文首先介绍Lock接口.ReentrantLock的类层次结构以及锁功能模板类AbstractQue ...

  6. ReentrantLock源码分析与理解

    在上面一篇分析ThreadExecutedPool的文章中我们看到线程池实现源码中大量使用了ReentrantLock锁,那么ReentrantLock锁的优势是什么?它又是怎么实现的呢? Reent ...

  7. 5.Lock接口及其实现ReentrantLock

    jdk1.7.0_79 在java.util.concurrent.locks这个包中定义了和synchronized不一样的锁,重入锁——ReentrantLock,读写锁——ReadWriteLo ...

  8. ReentrantLock和synchronized的性能对比

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytpo8 ReentrantLock和内部锁的性能对比     Reentran ...

  9. 并发编程(五)——AbstractQueuedSynchronizer 之 ReentrantLock源码分析

    本文将从 ReentrantLock 的公平锁源码出发,分析下 AbstractQueuedSynchronizer 这个类是怎么工作的,希望能给大家提供一些简单的帮助. AQS 结构 先来看看 AQ ...

随机推荐

  1. JavaScript学习 - 基础(五) - string/array/function/windows对象

    String对象 更详细转:http://www.w3school.com.cn/jsref/jsref_obj_string.asp //------------------------------ ...

  2. Dubbo高可用

    高可用:通过设计减少系统不能提供服务的时间 (1).zookeeper宕机 原因:zookeeper宕机 现象:zookeeper注册中心宕机,还可以消费dubbo暴露的服务. 健壮性: 监控中心宕掉 ...

  3. python3之协程

    1.协程的概念 协程,又称微线程,纤程.英文名Coroutine. 线程是系统级别的它们由操作系统调度,而协程则是程序级别的由程序根据需要自己调度.在一个线程中会有很多函数,我们把这些函数称为子程序, ...

  4. mysql系列三、mysql开启缓存、设置缓存大小、缓存过期机制

    一.开启缓存 mysql 开启查询缓存可以有两种方法来开启一种是使用set命令来进行开启,另一种是直接修改my.ini文件来直接设置都是非常的简单的哦. 开启缓存,设置缓存大小,具体实施如下: 1.修 ...

  5. Python3学习笔记16-错误和异常

    使用try...except可以处理异常 异常处理 import sys try: print('try...') r = 10/0 print('result:',r) except ZeroDiv ...

  6. Bootstrap3.0学习第四轮(排版)

    详情请查看http://aehyok.com/Blog/Detail/10.html 个人网站地址:aehyok.com QQ 技术群号:206058845,验证码为:aehyok 本文文章链接:ht ...

  7. Windows CreateFont:创建自己的字体

    原文地址:http://blog.csdn.net/softn/article/details/51718347 前面无论是使用文本输出函数还是 static 控件,字体都是默认的,比较丑陋,我们完全 ...

  8. 802.11 af 要点

    (1)TVWS工作在 VHF/UHF 频段,欧洲为 470-790MHZ, 美国为 54-698MHZ. (2)GDB(Geolocation Database):地理位置数据库,与其他802.11协 ...

  9. 破解idea

    2019最新注册码 地址:  http://idea.lanyus.com/ https://blog.csdn.net/best_luxi/article/details/81479820

  10. Redis Windows上下载安装

    其它的默认就可. public class RedisTest { public static void main(String[] args) { Jedis jedis = RedisPool.g ...