Java 并发编程(一) → LockSupport 详细说明

开心一刻

  今日忽然接到蚂蚁花呗消息推送的信息,说下一个月 9 号必须还贷要多少钱

  我也纳了闷了,我很长期不起作用蚂蚁花呗了,为什么会欠蚂蚁花呗钱?

  后边我一想,孩子近几天玩了我手机,是否他偷模用了我的蚂蚁花呗

  因此我寻找孩子问了起來

  我:孩子,是不是你用了我的蚂蚁花呗

  孩子:是的呀,爸,我也用了一点

  我:信用额度就剩二块了,你用了我用哪种?

  孩子:你用你爸的呗!

  我:...

  不对啊,女友也没有,哪儿的孩子?猛的被吓醒,白天的,我特么居然还做到了白日梦!

序言

  文中是根据 JDK1.8

  那麼这时 Java 进程与电脑操作系统进程的对应关系是 1:1 的,有兴趣爱好的能够 读一读:深层次聊一聊java进程实体模型完成?

  对于 Java 是不是在未来引进相近 Go 中的协同程序,进而完成 Java 进程与电脑操作系统进程的关联是 m:n,那就是将来的事,那么就将来再聊

  大家能明确的是:Java8 中,Java 进程与电脑操作系统进程是 1:1 的

LockSupport 介绍

  有关 LockSupport,大家对它觉得很生疏,由于大家在工作上非常少直接接触到它,但或多或少,大家都间接性采用过它

  LockSupport 是 JUC 包下很重要的一个java工具,大家讨论一下它的源代码简述:

    Basic thread blocking primitives for creating locks and other synchronization classes

    用以建立锁和别的同歩类的基本上进程堵塞原语

  JUC 包了的锁、同歩类基本上都依靠 LockSupport 完成进程的堵塞与唤起

  我们可以简易的觉得 LockSupport 对 Java 进程(电脑操作系统进程)的堵塞与唤起开展了封裝,简单化了开发者的每日任务

  permit(许可证书)

  LockSupport 的设计理念便是为每一个进程设定一个 permit,实际上便是一个值,类似 AQS 中的 state

  但 permit 沒有表明的存有于 LockSupport 的源代码中,而 state 却表明的存有于 AQS 的源代码中( private volatile int state; )

    permit 初始值(初值)是 0,permit 极小值是 0,最高值是 1;0 表明许可证书不能用,1 表明许可证书可以用

    若 permit 数值 0,则 park 方式 会堵塞当今进程,直到请求超时或有可以用的 permit;若 permit 为 1 ,则 park 方式 会将 permit 值设成 0,不容易堵塞当今进程

    无论 permit 的值是 0 或是 1,unpark 方式 会将 permit 设成 1,也便说数次 unpark (正中间沒有 park)后,permit 的值仍是 1

  那麼那么问题来了,permit 没有 LockSupport 中,那麼它在哪儿?

  实际上 permit 反映在 JVM 中,大家讨论一下在 Hotspot 中相匹配的源代码,在 /hotspot/src/share/vm/runtime/park.hpp 中有以下一段

class Parker : public os::PlatformParker {
private:
  volatile int _counter ;
  Parker * FreeNext ;
  JavaThread * AssociatedWith ; // Current association

public:
  Parker() : PlatformParker() {
    _counter       = 0 ;
    FreeNext       = NULL ;
    AssociatedWith = NULL ;
  }
protected:
  ~Parker() { ShouldNotReachHere(); }
public:
  // For simplicity of interface with Java, all forms of park (indefinite,
  // relative, and absolute) are multiplexed into one call.
  void park(bool isAbsolute, jlong time);
  void unpark();

  // Lifecycle operators
  static Parker * Allocate (JavaThread * t) ;
  static void Release (Parker * e) ;
private:
  static Parker * volatile FreeList ;
  static volatile int ListLock ;

};
View Code

  这一 volatile int _counter 便是 permit 的最底层实际完成

LockSupport 关键方式

  方式 很少,如下图

  

  关键分两大类:park 和 unpark ,大家对于这好多个方式 ,一个一个看来,留意多看看注解

  park

  会耗费 permit,若当今沒有可以用的 permit,则会堵塞当今进程

  park()

    方式 体比较简单

    简易的一行: UNSAFE.park(false, 0L); 有关 Unsafe,有兴趣爱好的能够 去掌握下:Java法术类:Unsafe运用分析

    只看这个编码,大家很不好看出什么,所幸有方式 注解,简单翻译一下

    1、除非是 permit 可以用,不然堵塞当今进程直到 permit 可以用

    2、假如 permit 可以用,会将 permit 设成 0,马上回到,不容易堵塞当今进程

    3、当 permit 不能用时,当今进程会被堵塞,直到产生下列三种状况

      3.1 别的进程启用 unpark 唤起此进程

      3.2 别的进程根据 Thread#interrupt 终断此进程

      3.3 该启用漏洞百出地(即没什么原因地)回到,可能是电脑操作系统出现异常造成的

    4、park() 不容易汇报是怎么回事造成的启用回到,有需要的话,入参需要在回到时自主查验是啥标准造成启用回到

  park(Object blocker)

    方式 体也非常简单

    作用与 park() 一样,仅仅多了个入参:Object blocker ,在进程被阻拦时纪录此目标,以容许监控和确诊专用工具鉴别进程被阻拦的缘故

    大家根据 jstack 指令,讨论一下 park() 和 park(Object blocker) 进程快照更新信息内容有什么不同

    实例编码:

    用 park() 时进程 t1 的快照更新信息内容以下

    用 park(Object blocker) 时进程 t1 的快照更新信息内容以下

    大家发觉 park(Object blocker) 多了一行: - parking to wait for <0x000000076bbb5108> (a java.lang.String) 

    自然 park(Object blocker) 不容易像实例中那麼应用(传个固定不动的字符串数组),传的肯定是更有意义的目标,大家讨论一下 JDK 中什么地方采用了它

    有兴趣的能够 看一看实际的编码,在其中的 this 实际是啥,它做为 blocker 有什么作用

  parkNanos(long nanos)

    nanos 表明等候的较大纳秒数;大家来翻译一下方式 的注解

    1、除非是 permit 可以用,不然堵塞当今进程直到 permit 可以用,或是等候的時间完毕

    2、假如 permit 可以用,会将 permit 设成 0,马上回到,不容易堵塞当今进程

    3、当 permit 不能用时,当今进程会被堵塞,直到产生下列四种状况

      3.1 别的进程启用 unpark 唤起此进程

      3.2 别的进程根据 Thread#interrupt 终断此进程

      3.3 历经特定的等待的时间,不容易无期限的等候下来

      3.4 该启用漏洞百出地(即没什么原因地)回到,可能是电脑操作系统出现异常造成的

    4、park() 不容易汇报是怎么回事造成的启用回到,有需要的话,入参需要在回到时自主查验是啥标准造成启用回到

    能够 看得出,作用与 park() 基本一致,仅仅多了一个等候时间

  parkNanos(Object blocker, long nanos)

    作用与 parkNanos(long nanos) 基本上一样,仅仅多了个 Object blocker 

    将 parkNanos(Object blocker, long nanos) 与 parkNanos(long nanos)  的关联与 park(Object blocker) 于 park() 的关联开展对比,就行了解了

    JDK 中有很多地区采用了 parkNanos(Object blocker, long nanos)

  parkUntil(long deadline)

    dealine 表明等候到的绝对时间,以ms为企业

    作用与 parkNanos(long nanos) 基本一致,仅仅 parkNanos(long nanos) 等候的是相对性时间(纳秒),而 parkUntil(long deadline) 等候的则是绝对时间点(ms)

  parkUntil(Object blocker, long deadline)

    作用与 parkUntil(long deadline),仅仅多了个 Object blocker

    将 parkUntil(Object blocker, long deadline) 与 parkUntil(long deadline) 的关联与 parkNanos(Object blocker, long nanos) 与 parkNanos(long nanos)  的关联开展目录,就行了解了

    JDK 中有一些地区采用了 parkUntil(Object blocker, long deadline) 

  unpark

  方式 体比较简单

  大家来翻一下它的注解

  1、使入参进程的 permit 可以用(将 permit 设成 1)

  2、假如入参进程正堵塞于 park,那麼会唤起入参进程,不然入参进程的下一次 park 不容易堵塞

  3、假如入参进程都还没运行,它不容易造成一切实际效果

  4、假如入参进程为null,它不容易造成一切实际效果

  JDK 中有很多地区采用了它

应用情景

  由于 JDK 早已给予了丰富多彩的 API,因此 大家平常基本上不容易立即应用 LockSupport,因此 许多人觉得 LockSupport 离大家很远

  其实不是,只需大家采用 JUC 下的类来开展并发编程,那麼就早已间接性采用了 LockSupport 了

  JUC 中进程的堵塞与唤起的完成,依靠的全是 LockSupport

  进程更替打印出

    它是小编以前碰到的一个面试问题,LockSupport 便是在其中的一个考试点,实际可查询:记一个有趣的面试问题 → 进程更替輸出难题

    用 LockSupport 是最佳的处理方法,不依赖于第三方的同歩值,编码简易,逻辑清晰,很好了解和完成

汇总

  1、park 分三类,每种分二种,官方网强烈推荐用带 blocker 主要参数的那一种

    park()、park(Object blocker)

    parkNanos(long nanos)、parkNanos(Object blocker, long nanos)

    parkUntil(long deadline)、parkUntil(Object blocker, long deadline)

  2、park 与 unpark 中间沒有严苛的启用顺序

    permit = 1 表明可以用,permit = 0 表明不能用;permit 归属于进程独享

    park 耗费 permit,将 permit 从 1 设成 0;unpark 则将 permit 设成 1,无论设定前的值是 1 或是 0

    permit 可以用,则 park 不容易堵塞当今进程,将 permit 设成 0,进程再次向下实行,不然 park 会堵塞当今进程

    unpark 会设定特定进程的 permit = 1,并唤起特定的进程

参照

  Java法术类:Unsafe运用分析

  JVM 普遍网上难题 → CPU 100%、内存泄露 难题清查

评论(0条)

刀客源码 游客评论