看了几篇关于这三者区别的文章,但都说的不够具体,自己去读了下源码,大概是清楚了三者的功能,不想了解源码的可以跳到最后看总结。

首先,ReentrantLock类中使用了大量的CAS操作,也就是CompareAndSwap原子操作,依靠硬件保证互斥与同步,然后说下interrupt()方法。每个线程都有一个interrupt标志。当线程在阻塞状态时,如sleep、wait、await(park)、join,此时如果对该进程调用interrupt()方法,该线程会被唤醒、interrupt标志被修改,并抛出InterruptedException异常(一旦捕捉到异常,立刻重置interrupt标志),因此上述的那些方法被要求为必须捕捉异常;当线程在运行中并未进入任何阻塞状态时,则interrupt()操作不会打断线程执行,只会修改该线程interrupt标志,此时可以通过Thread.interrupted()/实例.isInterrupted()查看并作出处理(前者会重置该标志位),通常使用while循环来检查。

一、 lock()方法

使用lock()获取锁,若CAS替换state成功,则直接标记本线程获取到了锁,然后返回。若获取失败,这时先将线程阻塞放入等待队列,然后跑一个for循环(如下代码),parkAndCheckInterrupt方法调用LockSupport.park()使线程进入阻塞状态,当它被被唤醒(前一个节点unpark唤醒or外部interrupt唤醒)时会调用tryAcquire继续尝试竞争锁。如果此时用CAS获取到了锁那么就返回,如果没获取到那么再次放入等待队列,如此循环。其间就算外部调用了interrupt(),循环也会继续走下去。一直到当前线程获取到了这个锁,此时才处理interrupt标志,若有,则执行自我调用 Thread.currentThread().interrupt(),结果如何取决于外层的处理。

    final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false;for (;;) {final Node p = node.predecessor();if (p == head && tryAcquire(arg)) { //如果竞争得到了锁setHead(node);p.next = null; // help GCfailed = false;return interrupted; //获取成功返回interrupted标志}// 只修改标志位,不做其他处理if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true;}} finally {if (failed)cancelAcquire(node);}}

其中parkAndCheckInterrupt()调用了LockSupport.park(),该方法使用Unsafe类将进程阻塞并放入等待队列,等待唤醒,和await()有点类似。

可以看到循环中检测到了interrupt标记,但是仅做 interrupted = true 操作,直到获取到了锁,才return interrupted,然后处理如下

   public final void acquire(int arg) {if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt(); // 执行Thread.currentThread().interrupt()}

二、 lockInterruptibly()方法

和lock()相比,lockInterruptibly()只有略微的修改,for循环过程中,如果检测到interrupt标志为true,则立刻抛出InterruptedException异常,这时程序便通过异常直接返回到最外层了,由外层继续处理,因此使用lockInterruptibly()时必须捕捉异常。lockInterruptibly()最终执行的方法如下:

    private void doAcquireInterruptibly(int arg)throws InterruptedException {final Node node = addWaiter(Node.EXCLUSIVE);boolean failed = true;try {for (;;) {final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCfailed = false;return; //获取成功返回}if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())throw new InterruptedException(); //直接抛出异常}} finally {if (failed)cancelAcquire(node);}}

三、 tryLock()方法

使用tryLock()尝试获取一次锁,若获取成功,标记下是该线程获取到了锁,然后返回true;若获取失败,此时直接返回false,之后的操作取决于外层,代码如下:

        final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}

else if 中的情况属于锁重入情况,即外层函数已经获取到这个锁了,内部的函数又再次对其进行获取,那么对计数器加一,不再循环加锁。

四、tryLock(long timeout, TimeUnit unit) 方法

还有一种情况,即使用tryLock(long timeout, TimeUnit unit)获取锁,此时有点像lockInterruptibly()的升级版,在获取锁失败后的循环中,它被放入等待队列并使用 LockSupport.parkNanos(this, nanosTimeout) 阻塞进程,同时timeout扣减每次循环消耗的时间,当timeout用尽时如果依然没有获取到锁,那么就返回false。对于循环期间收到的interrupt()的处理,这儿和lockInterruptibly()一样,一旦检测到,那么直接抛出异常。代码如下:

    private boolean doAcquireNanos(int arg, long nanosTimeout)throws InterruptedException {long lastTime = System.nanoTime();final Node node = addWaiter(Node.EXCLUSIVE);boolean failed = true;try {for (;;) {final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCfailed = false;return true; //获取成功返回}if (nanosTimeout <= 0)return false;if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold)LockSupport.parkNanos(this, nanosTimeout); //带timeout的阻塞long now = System.nanoTime();nanosTimeout -= now - lastTime;lastTime = now; // 更新timeoutif (Thread.interrupted())throw new InterruptedException(); //直接抛出异常}} finally {if (failed)cancelAcquire(node);}

总结

  1. lock()  阻塞式地获取锁,只有在获取到锁后才处理interrupt信息
  2. lockInterruptibly() 阻塞式地获取锁,立即处理interrupt信息,并抛出异常
  3. tryLock()  尝试获取一次锁,不管成功失败,都立即返回true、false
  4. tryLock(long timeout, TimeUnit unit)在timeout时间内阻塞式地获取锁,成功返回true,超时返回false,同时立即处理interrupt信息,并抛出异常

ReentrantLock中lock/trylock/lockInterruptibly方法的区别及源码解析相关推荐

  1. Java生鲜电商平台-电商中海量搜索ElasticSearch架构设计实战与源码解析

    Java生鲜电商平台-电商中海量搜索ElasticSearch架构设计实战与源码解析 生鲜电商搜索引擎的特点 众所周知,标准的搜索引擎主要分成三个大的部分,第一步是爬虫系统,第二步是数据分析,第三步才 ...

  2. java 中线程池的种类,原理以及源码解析(1)

    java 中的线程池创建都是Executors 类中提供的方法,并且方法返回线程池对象. Executors 源码: // // Source code recreated from a .class ...

  3. pytorch python区别_pytorch源码解析:Python层 pytorchmodule源码

    尝试使用了pytorch,相比其他深度学习框架,pytorch显得简洁易懂.花时间读了部分源码,主要结合简单例子带着问题阅读,不涉及源码中C拓展库的实现. 一个简单例子 实现单层softmax二分类, ...

  4. 用post方式获取html,httpclient中怎么使用post方法获取html的源码

    params=new ArrayList(); //添加要传递的参数 params.add(new BasicNameValuePair("par","HTTP_Clie ...

  5. hibernate中antlr对于hql生成抽象语法树源码解析

    Hibernate版本5.1.11FInal 以一句update语句作为例子. update com.tydhot.eninty.User set userName=:userName where u ...

  6. Java Executor源码解析(3)—ThreadPoolExecutor线程池execute核心方法源码【一万字】

    基于JDK1.8详细介绍了ThreadPoolExecutor线程池的execute方法源码! 上一篇文章中,我们介绍了:Java Executor源码解析(2)-ThreadPoolExecutor ...

  7. java lock unlock_【Java并发007】原理层面:ReentrantLock中lock()、unlock()全解析

    一.前言 Java线程同步两种方式,synchronized关键字和Lock锁机制,其中,AQS队列就是Lock锁实现公平加锁的底层支持. 二.AQS源码对于lock.lock()的实现 2.1 AQ ...

  8. 【代码】使用ReentrantLock还可以调用lockInterruptibly方法,可以对线程interrupt方法做出响应

    import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concu ...

  9. java foward_java 中sendredirect()和forward()方法的区别

    HttpServletResponse.sendRedirect与RequestDispatcher.forward方法都可以实现获取相应URL资源. sendRedirect实现请求重定向,forw ...

最新文章

  1. 人生如戏,请给我好一点儿的演技
  2. hcia是什么等级的证书_华为的HCNA,HCNP,HCIE认证证书都有什么用?
  3. mysql 优化配置 大批量数据插入_MYSQL开发性能研究——批量插入的优化措施
  4. 代码评审-如何保证缓存与数据库的读写一致性?
  5. 查找学生链表c语言,【查找链表面试题】面试问题:C语言实现学生… - 看准网...
  6. jQuery LigerUI 插件介绍及使用之ligerTree
  7. stm32基本入门(一)
  8. 哪种linux好,哪种LINUX好用
  9. SPI(Service Provider Interface)机制
  10. 第二届上汽零束SOA平台开发者大会揭幕,智能汽车生态加速落地
  11. C语言零基础学习日记
  12. php 时间戳 星座,十二个星座的时间划分与性格总括
  13. 《孙子兵法》【行军第九】
  14. Resnet50残差网络代码详解
  15. Hibernate_day03(转载传智播客的老师讲义,博主只图查看方便)
  16. 亚马逊笔试题目_亚马逊笔试题目
  17. axure的html按钮设置背景,Axure格式和样式功能详解
  18. 如何用revit打不开服务器文件,双击RVT文件无法在正确版本的Revit中打开项目
  19. 医疗器械热搜词TOP10
  20. 【SRE笔记 2022.9.7 linux进程相关命令及源码包安装】

热门文章

  1. CVPR2022 活体检测 Adaptive Transformers for Robust Few-shotCross-domain Face Anti-spoofing
  2. 救生艇,满载需要的最少数量船
  3. 数学建模常用算法—灰色关联分析法(GRA)
  4. Github+VNote搭建个人笔记
  5. 安卓驱动开发!从入门到精通系列Android高级工程师路线介绍,架构师必备技能
  6. 搜索全部mp3类型文件
  7. 【鸿蒙】 使用定时器做一个简单的抢红包小游戏
  8. php mkdir no space left on device,错误:chown的mkdir在Dockerfile中不起作用(带有php-fpm的nginx)...
  9. 如何挑选购买一把适合自己的尤克里里ukulele,新手扫雷必看!
  10. HEVC学习(二) —— HM的整体结构及一些基本概念