功能简介:
  • Java代码层面提供的锁机制,可做为Synchronized(jvm内置)的替代物,和Synchronized一样都是可重入的。
  • 与Synchronized相比较而言,ReentrantLock有以下优 势:支持公平/非公平锁、支持可中断的锁、支持非阻塞的tryLock(可超时)、支持锁条件、可跨代码块使用(一个地方加锁,另一个地方解锁),总之比 Synchronized更加灵活。但也有缺点,比如锁需要显示解锁、无法充分享用JVM内部性能提升带来的好处等等。
源码分析:
  • ReentrantLock实现了Lock接口,先来看下这个接口:
  1. public interface Lock {
  2. /**
  3. * 获取锁,如果锁无法获取,当前线程被阻塞,直到锁可以获取并获取成功为止。
  4. */
  5. void lock();
  6. /**
  7. * 在当前线程没有被中断的情况下获取锁。
  8. * 如果获取成功,方法结束。
  9. * 如果锁无法获取,当前线程被阻塞,直到下面情况发生:
  10. * 1.当前线程(被唤醒后)成功获取锁。
  11. * 2.当前线程被其他线程中断。
  12. */
  13. void lockInterruptibly() throws InterruptedException;
  14. /**
  15. * 如果当前锁是可用的,获取锁。
  16. * 获取成功后,返回true。
  17. * 如果当前锁不可用,返回false。
  18. */
  19. boolean tryLock();
  20. /**
  21. * 如果锁在给定超时时间内可用,并且当前线程没有被中断,那么获取锁。
  22. * 如果锁可用,获取锁成功并返回true。
  23. * 如果锁无法获取,当前线程被阻塞,直到下面情况发生:
  24. * 1.当前线程(被唤醒后)成功获取锁。
  25. * 2.当前线程被其他线程中断。
  26. * 3.指定的等待时间超时。
  27. */
  28. boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
  29. /**
  30. * 释放锁。
  31. */
  32. void unlock();
  33. /**
  34. * 返回一个和当前锁实例相关联的条件。
  35. * 当前线程必须首先获取锁后才能在锁条件上等待。
  36. * 一个Condition的await()方法调用会在等待之前自动释放锁,在等待结束
  37. * 前重新获取锁。
  38. */
  39. Condition newCondition();
  40. }
  • 之前分析AQS的时候提到过,基于AQS构建的同步机制都会使用内部帮助类继承AQS的方式构建,看下ReentrantLock中的同步机制:
  1. //内部同步机制的引用。
  2. private final Sync sync;
  3. /**
  4. * 这个锁实现的基本同步控制机制,下面会提供公平和非公平版本的子类。
  5. * 利用AQS的state来表示锁持有(重入)的次数。.
  6. */
  7. static abstract class Sync extends AbstractQueuedSynchronizer {
  8. private static final long serialVersionUID = -5179523762034025860L;
  9. /**
  10. * Performs {@link Lock#lock}. The main reason for subclassing
  11. * is to allow fast path for nonfair version.
  12. */
  13. abstract void lock();
  14. /**
  15. * 方法用来支持非公平的tryLock
  16. */
  17. final boolean nonfairTryAcquire(int acquires) {
  18. final Thread current = Thread.currentThread();
  19. int c = getState();
  20. if (c == 0) {
  21. //如果当前没有任何线程获取锁(锁可用),尝试设置state。
  22. if (compareAndSetState(0, acquires)) {
  23. //如果设置成功,将当前线程信息设置到AQS中(所有权关联)。
  24. setExclusiveOwnerThread(current);
  25. return true;
  26. }
  27. }
  28. //如果锁已经被持有,那么判断一下持有锁的线程是否为当前线程。
  29. else if (current == getExclusiveOwnerThread()) {
  30. //如果是当前线程在持有锁,那么这里累计一下重入次数。
  31. int nextc = c + acquires;
  32. if (nextc < 0) // overflow 重入次数最大不能超过int的最大值
  33. throw new Error("Maximum lock count exceeded");
  34. //设置到AQS的state中
  35. setState(nextc);
  36. return true;
  37. }
  38. //如果锁已经被持有,且持有线程不是当前线程,返回false。
  39. return false;
  40. }
  41. protected final boolean tryRelease(int releases) {
  42. //释放时,这里要减去重入次数。
  43. int c = getState() - releases;
  44. //判断控制权关系是否正确。
  45. if (Thread.currentThread() != getExclusiveOwnerThread())
  46. throw new IllegalMonitorStateException();
  47. boolean free = false;
  48. if (c == 0) {
  49. //如果当前线程完全释放了锁(重入次数为0)
  50. free = true;
  51. //解除所有权关系。
  52. setExclusiveOwnerThread(null);
  53. }
  54. //设置重入次数。
  55. setState(c);
  56. //返回是否释放成功(或者说是否完全释放)。
  57. return free;
  58. }
  59. protected final boolean isHeldExclusively() {
  60. // While we must in general read state before owner,
  61. // we don't need to do so to check if current thread is owner
  62. return getExclusiveOwnerThread() == Thread.currentThread();
  63. }
  64. final ConditionObject newCondition() {
  65. return new ConditionObject();
  66. }
  67. // Methods relayed from outer class
  68. final Thread getOwner() {
  69. return getState() == 0 ? null : getExclusiveOwnerThread();
  70. }
  71. final int getHoldCount() {
  72. return isHeldExclusively() ? getState() : 0;
  73. }
  74. final boolean isLocked() {
  75. return getState() != 0;
  76. }
  77. /**
  78. * Reconstitutes this lock instance from a stream.
  79. * @param s the stream
  80. */
  81. private void readObject(java.io.ObjectInputStream s)
  82. throws java.io.IOException, ClassNotFoundException {
  83. s.defaultReadObject();
  84. setState(0); // reset to unlocked state
  85. }
  86. }

接下来先看一下非公平版本的子类:

  1. /**
  2. * Sync object for non-fair locks
  3. */
  4. final static class NonfairSync extends Sync {
  5. private static final long serialVersionUID = 7316153563782823691L;
  6. /**
  7. * Performs lock. Try immediate barge, backing up to normal
  8. * acquire on failure.
  9. */
  10. final void lock() {
  11. //这里首先尝试一个短代码路径,直接CAS设置state,尝试获取锁。
  12. //相当于一个插队的动作(可能出现AQS等待队列里有线程在等待,但当前线程竞争成功)。
  13. if (compareAndSetState(0, 1))
  14. setExclusiveOwnerThread(Thread.currentThread());
  15. else
  16. acquire(1);//如果CAS失败,调用AQS的独占请求方法。
  17. }
  18. protected final boolean tryAcquire(int acquires) {
  19. //调用上面父类的nonfairTryAcquire方法。
  20. return nonfairTryAcquire(acquires);
  21. }
  22. }

再来先看一下公平版本的子类:

  1. /**
  2. * Sync object for fair locks
  3. */
  4. final static class FairSync extends Sync {
  5. private static final long serialVersionUID = -3000897897090466540L;
  6. final void lock() {
  7. acquire(1);
  8. }
  9. /**
  10. * 公平版本的tryAcquire。
  11. * 只有在递归(重入)或者同步队列中没有其他线程
  12. * 或者当前线程是等待队列中的第一个线程时才准许访问。
  13. */
  14. protected final boolean tryAcquire(int acquires) {
  15. final Thread current = Thread.currentThread();
  16. int c = getState();
  17. if (c == 0) {
  18. if (!hasQueuedPredecessors() &&
  19. compareAndSetState(0, acquires)) { //如果当前锁可用,且同步等待队列中没有其他线程,那么尝试设置state
  20. setExclusiveOwnerThread(current); //如果设置成功,相当于获取锁成功,设置所有权关系。
  21. return true;
  22. }
  23. }
  24. else if (current == getExclusiveOwnerThread()) {
  25. //如果当前线程已经持有该锁,那么累计重入次数。
  26. int nextc = c + acquires;
  27. if (nextc < 0)
  28. throw new Error("Maximum lock count exceeded");
  29. setState(nextc);
  30. return true;
  31. }
  32. return false;
  33. }
  34. }
       小总结一下:
       非公平版的锁-加锁操作
  1. 当前线程首先会无条件的执行一个CAS操作来获取锁,如果CAS操作成功,获取锁成功。
  2. 如果第1步没成功,当前会检查锁是否被其他线程持有,也就是锁是否可用。
  3. 如果没有其他线程持有锁,会以CAS的方式尝试获取锁,如果CAS操作成功,获取锁成功。
  4. 如果有其他线程持有锁,会判断一下持有锁的线程是否为当前线程,如果是当前线程,重入次数+1,获取锁成功。
  5. 根据AQS的分析,上述2、3、4步会执行多次,如果最终获取锁失败,当前线程会被阻塞,等待其他线程执行解锁操作将其唤醒。
       公平版的锁-加锁操作
  1. 当前线程首先会检查锁是否被其他线程持有,并且当前同步等待队列里有没有其他线程在等待。
  2. 如果没有其他线程持有锁,且同步等待队列里没有其他线程,会以CAS的方式尝试获取锁,如果CAS操作成功,获取锁成功。
  3. 如果有其他线程持有锁,会判断一下持有锁的线程是否为当前线程,如果是当前线程,重入次数+1,获取锁成功。
  4. 根据AQS的分析,上述1、2、3步会执行多次,如果最终获取锁失败,当前线程会被阻塞,等待其他线程执行解锁操作将其唤醒。
       非公平版和公平版锁的解锁操作一样
  1. 当前线程首先将锁重入次数减1(AQS的state),如果减1后结果为0,将当前同步器的线程信息置空,并唤醒同步等待队列中队头的等待线程。
  2. 如果第1步中,重入次数减1后结果不为0(说明当前线程还持有当前锁),方法结束。
  • 有了内部的基础同步机制,ReentrantLock的实现就很简单了,直接看代码:
  1. /**
  2. * 默认情况下构建非公平锁。
  3. */
  4. public ReentrantLock() {
  5. sync = new NonfairSync();
  6. }
  7. /**
  8. * 根据给定的公平策略生成相应的实例。
  9. *
  10. * @param fair {@code true} if this lock should use a fair ordering policy
  11. */
  12. public ReentrantLock(boolean fair) {
  13. sync = (fair)? new FairSync() : new NonfairSync();
  14. }
  15.  
  16. public void lock() {
  17. sync.lock();
  18. }
  19.  
  20. public void lockInterruptibly() throws InterruptedException {
  21. sync.acquireInterruptibly(1);
  22. }
  23.  
  24. public boolean tryLock() {
  25. return sync.nonfairTryAcquire(1);
  26. }
  27.  
  28. public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
  29. return sync.tryAcquireNanos(1, unit.toNanos(timeout));
  30. }
  31.  
  32. public void unlock() {
  33. sync.release(1);
  34. }
  35.  
  36. public Condition newCondition() {
  37. return sync.newCondition();
  38. }

最后看一下一些支持监测的方法:

  1. /**
  2. * 获取当前线程的对当前锁的持有(重入)次数。
  3. */
  4. public int getHoldCount() {
  5. return sync.getHoldCount();
  6. }
  7. /**
  8. * 判断当前锁是否被当前线程持有。
  9. */
  10. public boolean isHeldByCurrentThread() {
  11. return sync.isHeldExclusively();
  12. }
  13. /**
  14. * 判断当前锁是否被(某个线程)持有。
  15. */
  16. public boolean isLocked() {
  17. return sync.isLocked();
  18. }
  19. /**
  20. * 当前锁是否为公平锁。
  21. */
  22. public final boolean isFair() {
  23. return sync instanceof FairSync;
  24. }
  25. /**
  26. * 获取持有当前锁的线程。
  27. */
  28. protected Thread getOwner() {
  29. return sync.getOwner();
  30. }
  31. /**
  32. * 判断是否有线程在当前锁的同步等待队列中等待。
  33. */
  34. public final boolean hasQueuedThreads() {
  35. return sync.hasQueuedThreads();
  36. }
  37. /**
  38. * 判断给定的线程是否在当前锁的同步等待队列中等待。
  39. */
  40. public final boolean hasQueuedThread(Thread thread) {
  41. return sync.isQueued(thread);
  42. }
  43. /**
  44. * 获取当前锁的同步等待队列中的等待线程(估计)数量。
  45. */
  46. public final int getQueueLength() {
  47. return sync.getQueueLength();
  48. }
  49. /**
  50. * 获取当前锁的同步等待队列中等待的线程。
  51. */
  52. protected Collection<Thread> getQueuedThreads() {
  53. return sync.getQueuedThreads();
  54. }
  55. /**
  56. * 判断是否有线程在给定条件的条件等待队列上等待。
  57. */
  58. public boolean hasWaiters(Condition condition) {
  59. if (condition == null)
  60. throw new NullPointerException();
  61. if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
  62. throw new IllegalArgumentException("not owner");
  63. return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
  64. }
  65. /**
  66. * 获取给定条件的条件等待队列中等待线程的(估计)数量。
  67. */
  68. public int getWaitQueueLength(Condition condition) {
  69. if (condition == null)
  70. throw new NullPointerException();
  71. if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
  72. throw new IllegalArgumentException("not owner");
  73. return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
  74. }
  75. /**
  76. * 获取给定条件的条件等待队列中等待线程。
  77. */
  78. protected Collection<Thread> getWaitingThreads(Condition condition) {
  79. if (condition == null)
  80. throw new NullPointerException();
  81. if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
  82. throw new IllegalArgumentException("not owner");
  83. return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
  84. }

Jdk1.6 JUC源码解析(7)-locks-ReentrantLock的更多相关文章

  1. Jdk1.6 JUC源码解析(12)-ArrayBlockingQueue

    功能简介: ArrayBlockingQueue是一种基于数组实现的有界的阻塞队列.队列中的元素遵循先入先出(FIFO)的规则.新元素插入到队列的尾部,从队列头部取出元素. 和普通队列有所不同,该队列 ...

  2. Jdk1.6 JUC源码解析(6)-locks-AbstractQueuedSynchronizer

    功能简介: AbstractQueuedSynchronizer(以下简称AQS)是Java并发包提供的一个同步基础机制,是并发包中实现Lock和其他同步机制(如:Semaphore.CountDow ...

  3. Jdk1.6 JUC源码解析(1)-atomic-AtomicXXX

    转自:http://brokendreams.iteye.com/blog/2250109 功能简介: 原子量和普通变量相比,主要体现在读写的线程安全上.对原子量的是原子的(比如多线程下的共享变量i+ ...

  4. Jdk1.6 JUC源码解析(13)-LinkedBlockingQueue

    功能简介: LinkedBlockingQueue是一种基于单向链表实现的有界的(可选的,不指定默认int最大值)阻塞队列.队列中的元素遵循先入先出 (FIFO)的规则.新元素插入到队列的尾部,从队列 ...

  5. 【JUC源码解析】ScheduledThreadPoolExecutor

    简介 它是一个线程池执行器(ThreadPoolExecutor),在给定的延迟(delay)后执行.在多线程或者对灵活性有要求的环境下,要优于java.util.Timer. 提交的任务在执行之前支 ...

  6. 【JUC源码解析】SynchronousQueue

    简介 SynchronousQueue是一种特殊的阻塞队列,该队列没有容量. [存数据线程]到达队列后,若发现没有[取数据线程]在此等待,则[存数据线程]便入队等待,直到有[取数据线程]来取数据,并释 ...

  7. 【JUC源码解析】ForkJoinPool

    简介 ForkJoin 框架,另一种风格的线程池(相比于ThreadPoolExecutor),采用分治算法,工作密取策略,极大地提高了并行性.对于那种大任务分割小任务的场景(分治)尤其有用. 框架图 ...

  8. 【JUC源码解析】DelayQueue

    简介 基于优先级队列,以过期时间作为排序的基准,剩余时间最少的元素排在队首.只有过期的元素才能出队,在此之前,线程等待. 源码解析 属性 private final transient Reentra ...

  9. 【JUC源码解析】CyclicBarrier

    简介 CyclicBarrier,一个同步器,允许多个线程相互等待,直到达到一个公共屏障点. 概述 CyclicBarrier支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后 ...

随机推荐

  1. iOS 将对象的属性和属性值拆分成key、value,通过字符串key来获取该属性的值

    这篇博客光看标题或许就会产生疑问,某个对象,只要它存在某个属性,且值不是空的,不就能直接用点方法获取吗,为什么要拆分成key和value多此一举呢?下面,我用一个例子告诉大家,既然这方法是存在的,那就 ...

  2. css2.1实现圆角边框

    虽然css3的border-radius实现圆角很简单,但是我还是认为css2.1中好多技术还是很值得学习的,我也是后来才知道这就是传说中的滑动门技术.脑洞大开啊 附上demo <!DOCTYP ...

  3. Nginx+Tomcat动静分离脚本

    #!/bin/bashsetenforce 0systemctl stop firewalldtar -zxvf nginx-1.8.0.tar.gz -C /usr/src/ cd /usr/src ...

  4. Struts2的类型转换(上)

    传统的类型转换.(略) Struts2中提供了内置的基本的类型转换器,可以实现基本类型的自动转换,包括: Integer, Float, Double, Decimal Date and Dateti ...

  5. 树莓派Raspberry实践笔记-简单方法安装minicom

    [原创链接]:http://www.cnblogs.com/atsats/p/6665566.html 本文结合最新的Raspbain jessie,使用图形化的方式安装一款软件:串口调试工具mini ...

  6. 老李秘技:loadrunner11是否还支持dblib协议?

    老李秘技:loadrunner11是否还支持dblib协议?   Loadrunner11不再支持Sybase CTLIB 和 DBLIB协议 在loadrunner安装文件中找到*. LRP文件,位 ...

  7. 老李推荐:第2章2节《MonkeyRunner源码剖析》了解你的测试对象: NotePad窗口Activity之NotesList简介

    老李推荐:第2章2节<MonkeyRunner源码剖析>了解你的测试对象: NotePad窗口Activity之NotesList简介   NotePad窗口Activity之NotesL ...

  8. (转)Java ConcurrentModificationException异常原因和解决方法

    转载自:http://www.cnblogs.com/dolphin0520/p/3933551.html 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会 ...

  9. 4.Java集合总结系列:Map接口及其实现

    一.Map接口 Map集合的特点是:通过key值找到对应的value值,key值是唯一的,value可以重复.Map中的元素是无序的,但是也有实现了排序的Map实现类,如:TreeMap. 上面Map ...

  10. Asp.NetCore之组件写法

    本章内容和大家分享的是Asp.NetCore组件写法,在netcore中很多东西都以提供组件的方式来使用,比如MVC架构,Session,Cache,数据库引用等: 这里我也通过调用验证码接口来自定义 ...