Java并发编程系列之LockSupport的用法

作者:smileNicky 时间:2022-04-07 06:48:58 

目录
  • 1、什么是LockSupport?

  • 2、两类基本API

  • 3、LockSupport本质

  • 4、LockSupport例子

  • 5、LockSupport源码

  • 总结

1、什么是LockSupport?

LockSupport是用于创建锁和其他同步类的基本线程阻塞原语

2、两类基本API

LockSupport提供了两类最基本的API:

block线程类:一般都是以pack开头的方法名,pack*(...)

pack方法有两个重载的版本:blocker是一个对象,用于指定阻塞哪个对象。不知道的情况,默认以锁对象自己this为blocker


public static void park();
public static void park(Object blocker);

拓展parkNanos函数


public static void parkNanos(Object blocker, long nanos) {
   if (nanos > 0) {
       // 获取当前线程
       Thread t = Thread.currentThread();
       // 设置Blocker
       setBlocker(t, blocker);
       // 获取许可,并设置了时间
       UNSAFE.park(false, nanos);
       // 设置许可,重新设置blocker为null,避免unpack,获取的blocker为之前设置的
       setBlocker(t, null);
   }
}

nanos参数表示相对时间,表示等待多长时间

parkUntil函数:表示在指定的时限前禁用当前线程,deadline参数表示绝对时间,表示指定的时间


public static void parkUntil(Object blocker, long deadline) {
   // 获取当前线程
   Thread t = Thread.currentThread();
   // 设置Blocker
   setBlocker(t, blocker);
   UNSAFE.park(true, deadline);
   // 设置Blocker为null
   setBlocker(t, null);
}

unBlock线程类:unpack(Thread)

unpack方法用于释放许可,指定线程可以继续运行。

3、LockSupport本质

LockSupport是一个许可的信号量机制,pack消费,unpack放入,放入也是仅一个,不累计。例如,调用unpack放入一个信号量,多次调用,这个是不会累计信号量的,pack调用之后会消费

4、LockSupport例子

例子:如何控制两个线程依次打印1、2、3、4、5、6、…


import java.util.concurrent.locks.LockSupport;
public class LockSupportExample {
   private static final int total = 10;
   private  static int i = 0;
   static Thread t1 , t2;
   public static void main(String[] args) {
       t1 = new Thread(() ->{
           while (i < total) {
               System.out.println("t1:" + (++i));
               LockSupport.unpark(t2);
               LockSupport.park();
           }
       });
       t2 = new Thread(() -> {
           while (i < total) {
               LockSupport.park();
               System.out.println("t2:" + (++i));
               LockSupport.unpark(t1);
           }
       });
       t1.start();
       t2.start();
   }
}

打印:

t1: 1
t2: 2
t1:3
t2:4
t1:5
t2:6
t1:7
t2:8
t1:9
t2:10

5、LockSupport源码


public class LockSupport {
   // Hotspot implementation via intrinsics API
   private static final sun.misc.Unsafe UNSAFE;
   private static final long parkBlockerOffset;
   private static final long SEED;
   private static final long PROBE;
   private static final long SECONDARY;
   static {
       try {
           // 获取Unsafe实例
           UNSAFE = sun.misc.Unsafe.getUnsafe();
           // 线程类的class对象
           Class<?> tk = Thread.class;
           // 获取Thread的parkBlocker字段的内存偏移地址
           parkBlockerOffset = UNSAFE.objectFieldOffset
               (tk.getDeclaredField("parkBlocker"));
           // 获取Thread的threadLocalRandomSeed字段的内存偏移地址
           SEED = UNSAFE.objectFieldOffset
               (tk.getDeclaredField("threadLocalRandomSeed"));
           // 获取Thread的threadLocalRandomProbe字段的内存偏移地址
           PROBE = UNSAFE.objectFieldOffset
               (tk.getDeclaredField("threadLocalRandomProbe"));
           // 获取Thread的threadLocalRandomSecondarySeed字段的内存偏移地址
           SECONDARY = UNSAFE.objectFieldOffset
               (tk.getDeclaredField("threadLocalRandomSecondarySeed"));
       } catch (Exception ex) { throw new Error(ex); }
   }
}

pack方法的源码:


public static void park(Object blocker) {
   // 获取当前线程
   Thread t = Thread.currentThread();
   // 设置Blocker
   setBlocker(t, blocker);
   // 获取许可
   UNSAFE.park(false, 0L);
   // 重新可运行后再此设置Blocker为null,避免unpack获取到上一个设置的setBlocker(t, blocker);
   setBlocker(t, null);
}

unpack的源码:


public static void unpark(Thread thread) {
   if (thread != null) // 线程为不空
       UNSAFE.unpark(thread); // 释放该线程许可
}

可以看出,不管是pack的源码还是unpack的源码都是通过Unsafe的底层api实现的

sun.misc.Unsafe可以直接进行底层非安全操作的工具类

主要提供如下操作:

  • 线程挂起与恢复

  • CAS操作

  • 操纵对象属性

  • 操纵数组元素

  • 直接操纵内存

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

来源:https://smilenicky.blog.csdn.net/article/details/121102818

标签:Java,LockSupport
0
投稿

猜你喜欢

  • 搭建MyBatis-Plus框架并进行数据库增删改查功能

    2023-11-09 04:33:43
  • Java+swing实现抖音上的表白程序详解

    2023-11-15 21:34:08
  • Spring中Bean的加载与SpringBoot的初始化流程详解

    2022-12-18 05:02:18
  • Android中Blade的使用方法

    2023-04-25 11:29:54
  • Android 4.0 设置全屏修改的解决方法

    2022-11-24 17:08:12
  • Android 清除SharedPreferences 产生的数据(实例代码)

    2023-07-06 15:48:52
  • 浅谈Mybatis获取参数值的方式

    2022-09-22 07:56:21
  • 构建多模块的Spring Boot项目步骤全纪录

    2022-02-20 04:28:44
  • Maven发布封装到中央仓库时候报错:no default secret key

    2022-09-18 17:48:43
  • 详解SpringBoot中Session超时原理说明

    2022-01-24 06:40:49
  • Java异步处理机制实例详解

    2022-05-12 01:39:37
  • 详解Java中Math.round()的取整规则

    2022-10-20 14:22:01
  • springboot自动配置原理以及spring.factories文件的作用详解

    2021-12-20 20:19:27
  • C#-WinForm跨线程修改UI界面的示例

    2022-01-14 13:25:44
  • C#将Sql数据保存到Excel文件中的方法

    2023-11-11 08:34:26
  • 利用C#实现绘制出地球旋转效果

    2022-10-01 14:21:27
  • 利用Java Set 去除重复object的方法

    2023-07-27 09:41:57
  • Android自定义TextView跑马灯效果

    2023-08-07 01:14:57
  • 非常适合新手学生的Java线程池超详细分析

    2023-03-03 11:59:54
  • 深入学习java中的Groovy 和 Scala 类

    2023-04-09 10:51:29
  • asp之家 软件编程 m.aspxhome.com