文章目录

  • 概述
  • 主要方法
    • void park()
    • void unpark(Thread thread)
    • 小示例 park() & unpark(Thread thread)
    • void parkNanos(long nanos)
    • park(Object blocker)
    • void parkNanos(Object blocker, long nanos)
    • void parkUntil(Object blocker, long deadline)
  • 示例


概述

位于rt.jar包的java.util.concurrent.locks目录中, 主要作用是挂起和唤醒线程,该工具类是创建锁和其他同步类的基础。

LockSupport类与每个使用它的线程都会关联一个许可证,在默认情况下调用LockSupport类的方法的线程是不持有许可证的。


主要方法

void park()

如果调用park方法的线程已经拿到了与LockSupport关联的许可证,则调用LockSupport.park()时会马上返回,否则调用线程会被禁止参与线程的调度,也就是会被阻塞挂起。

    public static void main(String[] args) {System.out.println("begin park");LockSupport.park();System.out.println("end park");}

直接在main函数里面调用park方法,最终只会输出begin park!,然后当前线程被挂起,这是因为在默认情况下调用线程是不持有许可证的。

在其他线程调用 unpark(Thread thread)方法并且将当前线程作为参数时,调用park方法而被阻塞的线程会返回。

另外,如果其他线程调用了阻塞线程的interrupt()方法,设置了中断标志或者线程被虚假唤醒,则阻塞线程也会返回。

  • 所以在调用park方法时最好也使用循环条件判断方式。

  • 需要注意的是,因调用park()方法而被阻塞的线程被其他线程中断而返回时并不会抛出InterruptedException异常。


void unpark(Thread thread)

当一个线程调用unpark时,如果参数thread线程没有持有thread与LockSupport类关联的许可证,则让thread线程持有。

如果thread之前因调用park()而被挂起,则调用unpark后,该线程会被唤醒如果thread之前没有调用park,则调用unpark方法后,再调用park方法,其会立刻返回。修改代码如下。

   public static void main(String[] args) {System.out.println("begin park");LockSupport.unpark(Thread.currentThread());LockSupport.park();System.out.println("end park");}


小示例 park() & unpark(Thread thread)

 public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{System.out.println("child thread begin to park");LockSupport.park();System.out.println("child thread unpark");});// 启动子线程thread.start();// 主线程休眠1sTimeUnit.SECONDS.sleep(1);// 调用unpark方法让thread持有许可证,然后park方法返回System.out.println("main thread begin unpark ");LockSupport.unpark(thread);}

  • 首先创建了一个子线程thread,子线程启动后调用park方法,由于在默认情况下子线程没有持有许可证,因而它会把自己挂起

  • 主线程休眠1s是为了让主线程调用unpark方法前让子线程输出child thread begin park!并阻塞

  • 主线程然后执行unpark方法,参数为子线程,这样做的目的是让子线程持有许可证,然后子线程调用的park方法就返回了

park方法返回时不会告诉你因何种原因返回,所以调用者需要根据之前调用park方法的原因,再次检查条件是否满足,如果不满足则还需要再次调用park方法

例如,根据调用前后中断状态的对比就可以判断是不是因为被中断才返回的。

为了说明调用park方法后的线程被中断后会返回,我们修改上面的例子代码,删除LockSupport.unpark(thread);,然后添加thread.interrupt();


import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/12/5 8:53* @mark: show me the code , change the world*/
public class LockSupportDemo {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{System.out.println("child thread begin to park");// 调用park方法,挂起自己,只有被中断才推出循环while(!Thread.currentThread().isInterrupted()){LockSupport.park();}System.out.println("child thread unpark");});// 启动子线程thread.start();// 主线程休眠1sTimeUnit.SECONDS.sleep(1);// 中断子线程thread.interrupt();}
}

在如上代码中,只有中断子线程,子线程才会运行结束,如果子线程不被中断,即使你调用unpark(thread)方法子线程也不会结束。


void parkNanos(long nanos)

和park方法类似,如果调用park方法的线程已经拿到了与LockSupport关联的许可证,则调用LockSupport.parkNanos(long nanos)方法后会马上返回。该方法的不同在于,如果没有拿到许可证,则调用线程会被挂起nanos时间后修改为自动返回。

另外park方法还支持带有blocker参数的方法void park(Object blocker)方法,当线程在没有持有许可证的情况下调用park方法而被阻塞挂起时,这个blocker对象会被记录到该线程内部。

使用诊断工具可以观察线程被阻塞的原因,诊断工具是通过调用getBlocker(Thread)方法来获取blocker对象的,所以JDK推荐我们使用带有blocker参数的park方法,并且blocker被设置为this,这样当在打印线程堆栈排查问题时就能知道是哪个类被阻塞了

public class TestPark {public static void main(String[] args) {TestPark testPark = new TestPark();testPark.parkTest();}private void parkTest() {// 调用park 挂起自己LockSupport.park();}
}

运行代码后,使用jstack pid命令查看线程堆栈时可以看到如下输出结果。

使用带blocker参数的park方法,线程堆栈可以提供更多有关阻塞对象的信息。


park(Object blocker)

来看下源码

public static void park(Object blocker) {// 当前线程Thread t = Thread.currentThread();// 设置线程的blocker变量setBlocker(t, blocker);// 挂起线程UNSAFE.park(false, 0L);// 线程被激活后,清除blocker变量,因为一般都是在线程阻塞的时候才分析原因setBlocker(t, null);}

Thread类里面有个变量volatile Object parkBlocker,用来存放park方法传递的blocker对象,也就是把blocker变量存放到了调用park方法的线程的成员变量里面。


void parkNanos(Object blocker, long nanos)

相比park(Object blocker) 方法多了个超时时间。


void parkUntil(Object blocker, long deadline)

    public static void parkUntil(Object blocker, long deadline) {Thread t = Thread.currentThread();setBlocker(t, blocker);UNSAFE.park(true, deadline);setBlocker(t, null);}

其中参数deadline的时间单位为ms,该时间是从1970年到现在某一个时间点的毫秒值。这个方法和parkNanos(Object blocker, long nanos)方法的区别是,后者是从当前算等待nanos秒时间,而前者是指定一个时间点,比如需要等到2021.12.05日 12:00:00,则把这个时间点转换为从1970年到这个时间点的总毫秒数。


示例


import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/12/5 9:42* @mark: show me the code , change the world*/
public class FIFOMutex {private final AtomicBoolean locked = new AtomicBoolean(false);private final Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>();public void lock(){boolean wasInterrupted = false ;// 当期线程Thread current = Thread.currentThread();waiters.add(current);// 1 只有队首的线程可以获取锁while(waiters.peek() != current || !locked.compareAndSet(false,true)){LockSupport.park(this);if (Thread.interrupted()) { // 2wasInterrupted = true;}}waiters.remove();if (wasInterrupted){ // 3current.interrupt();}}public void unlock(){locked.set(false);LockSupport.unpark(waiters.peek());}}

这是一个先进先出的锁,也就是只有队列的首元素可以获取锁。

  • 在代码(1)处,如果当前线程不是队首或者当前锁已经被其他线程获取,则调用park方法挂起自己。

  • 然后在代码(2)处判断,如果park方法是因为被中断而返回,则忽略中断,并且重置中断标志,做个标记,然后再次判断当前线程是不是队首元素或者当前锁是否已经被其他线程获取,如果是则继续调用park方法挂起自己。

  • 然后在代码(3)中,判断标记,如果标记为true则中断该线程,这个怎么理解呢?其实就是其他线程中断了该线程,虽然我对中断信号不感兴趣,忽略它,但是不代表其他线程对该标志不感兴趣,所以要恢复下。

Java Review - 并发编程_LockSupport相关推荐

  1. Java Review - 并发编程_ 回环屏障CyclicBarrier原理源码剖析

    文章目录 Pre 小Demo 类图结构 CyclicBarrier核心方法源码解读 int await() int await(long timeout, TimeUnit unit) int dow ...

  2. Java Review - 并发编程_ScheduledThreadPoolExecutor原理源码剖析

    文章目录 概述 类结构 核心方法&源码解析 schedule(Runnable command, long delay,TimeUnit unit) scheduleWithFixedDela ...

  3. Java Review - 并发编程_ArrayBlockingQueue原理源码剖析

    文章目录 概述 类图结构 构造函数 主要方法源码解析 offer操作 put操作 poll操作 take操作 peek操作 size 小结 概述 Java Review - 并发编程_LinkedBl ...

  4. Java Review - 并发编程_LinkedBlockingQueue原理源码剖析

    文章目录 概述 类图结构 主要方法 offer操作 概述 Java Review - 并发编程_ConcurrentLinkedQueue原理&源码剖析 介绍了使用CAS算法实现的非阻塞队列C ...

  5. Java Review - 并发编程_读写锁ReentrantReadWriteLock的原理源码剖析

    文章目录 ReentrantLock VS ReentrantReadWriteLock 类图结构 非公平的读写锁实现 写锁的获取与释放 void lock() void lockInterrupti ...

  6. Java Review - 并发编程_原子操作类LongAdder LongAccumulator剖析

    文章目录 概述 小Demo 源码分析 重要的方法 long sum() reset sumThenReset longValue() add(long x) longAccumulate(long x ...

  7. Java Review - 并发编程_前置知识二

    文章目录 What's 多线程并发编程 线程安全问题 共享变量的内存可见性问题 synchronized synchronized的内存语义 volatile - 解决内存可见性 一般在什么时候才使用 ...

  8. Java Review - 并发编程_DelayQueue原理源码剖析

    文章目录 概述 类图结构 小Demo 核心方法&源码解读 offer操作 take操作 poll操作 size操作 小结 概述 DelayQueue并发队列是一个无界阻塞延迟队列,队列中的每个 ...

  9. Java Review - 并发编程_并发List_CopyOnWriteArrayList源码剖析

    文章目录 概述 源码解析 初始化 添加元素 获取指定位置元素 修改指定元素 删除元素 弱一致性的迭代器 CopyOnWriteArrayList 是如何实现弱一致性的 小结 概述 并发包中的并发Lis ...

最新文章

  1. opencv图像操作:读取,裁剪,保存,缩放,遍历和读取文件夹图片
  2. 面试常问 Java基础 冒泡排序
  3. 路由异常的起源-如何影响最终用户?——Vecloud微云
  4. 软考可以一次报两门吗
  5. 1.1.3开启线程(Starting a Thread)
  6. [BZOJ4303]数列
  7. react privateRoute
  8. C# 三菱PLC上位机开发环境搭建
  9. c# 高级开发应用:防止界面卡死之Application.DoEvents应用
  10. 有限元分析试题:ANSYS有限元分析中级培训考题及其答案解析(填空+选择+判断+解答)
  11. echarts世界地图各个国家及中国城市的经纬度数组
  12. 计算机专业在医院的工作,探析计算机在医院工作的应用
  13. Redis设计与实现(一)| 数据结构 对象
  14. w8ndows 秒表,关闭 Windows Search,Win8 能变快?
  15. 江苏大学京江学院计算机,江苏大学京江学院
  16. Django推导Django内部模块:wsgiref与jinja2
  17. NSG2-一个很好用的ns2的tcl脚本自动生成软件
  18. 魔兽世界用宏显示服务器时间,网上找的宏用后不能显示冷却时间为什么啊法师 – 手机爱问...
  19. 基于网格化的主干光缆纤芯配置模型
  20. 微信公众号网页开发——实用真机调试

热门文章

  1. 元素对应到html源代码,【整理】用Chrome或Chromium查看百度首页中各元素的html源码...
  2. java怎么让表格的字段相乘,excel表格怎么让数据相乘-如何在excel表格中设置乘法公式...
  3. android 之intent(意图)详解
  4. 矩阵分解法做推荐系统
  5. 组态王怎么做超级曲线_鱼怎么做才好吃?试试这个方法,吃着过瘾,还超级下饭!(收藏)...
  6. vue 改变domclass_基于 vue 开发甘特图组件的心路历程(兼设计分享)
  7. LeetCode题组:第26题-删除排序数组中的重复项
  8. ubuntu 12.04 联想thinkpad e430 安装wifi驱动
  9. 浅谈百度新一代query-ad 推荐引擎如何提升广告收益率
  10. 数据仓库系列篇——唯品会大数据架构