JUC之LockSupport构建同步组件的基本工具

一、前言

  LockSupport工具类用于阻塞或唤醒线程。LockSupport定义了一组的公共静态方法,这些方法提供了最基本的线程组阻塞和唤醒功能,而LockSupport也成为构建同步组件的基础工具。

  LockSupport定义了一组以park开头的方法用来阻塞当前线程,以及unpark(Thread thread)方法来唤醒一个被阻塞的线程。

二、源码分析

2.1 属性

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<?> 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); }
    }
}

2.2 构造器

// 私有构造函数,无法被实例化

private LockSupport() {}

2.3 方法

  LockSupport方法都是基于Unsafe类中定义的park和unpark方法

① park方法

  阻塞当前线程,如果调用unpark(Thread thread) 方法或者当前线程被中断,才能从park() 方法返回

//获取许可,设置时间无限长,直到可以获取许可
public static void park() {
     UNSAFE.park(false, 0L);//调用本地方法park
}public static void park(Object blocker) {
        Thread t = Thread.currentThread(); //当前线程
        setBlocker(t, blocker); //设置Blocker
        UNSAFE.park(false, 0L); //调用本地方法park
        setBlocker(t, null); //重新可运行后再次设置Blocker
}

② parkNanos方法

  阻塞当前线程,最长不超过nanos纳秒,返回条件在park() 的基础上增加了超时返回

public static void parkNanos(long nanos) {
        if (nanos > 0)  //时间要大于0
            UNSAFE.park(false, nanos); //给定时间阻塞
    }
public static void parkNanos(Object blocker, long nanos) {
        if (nanos > 0) { //时间大于0
            Thread t = Thread.currentThread(); //当前线程
            setBlocker(t, blocker); //设置Blocker
            UNSAFE.park(false, nanos); //设置指定时间阻塞
            setBlocker(t, null); //设置Blocker
        }
    }

③ parkUntil方法

  阻塞当前线程,知道deadline时间(从1970年开始到deadline时间的毫秒数)

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

上面其中三个方法中,都连续调用链setBlocker() ,为什么呢?

1. 调用park函数时,当前线程首先设置好parkBlocker字段,然后再调用Unsafe的park函数,此后,当前线程就已经阻塞了;

2. 等待该线程的unpark函数被调用,所以后面的一个setBlocker函数无法运行,unpark函数被调用,该线程获得许可后,就可以继续运行了;

3. 也就运行第二个setBlocker,把该线程的parkBlocker字段设置为null,这样就完成了整个park函数的逻辑。

如果没有第二个setBlocker,那么之后没有调用park(Object blocker),而直接调用getBlocker函数,得到的还是前一个park(Object blocker)设置的blocker,显然是不符合逻辑的。总之,必须要保证在park(Object blocker)整个函数执行完后,该线程的parkBlocker字段又恢复为null。

④ unpark方法

  唤醒处于阻塞状态的线程thread

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

⑤ setBlocker() 和 getBlocker()

// 设置当前线程阻塞的原因,可以方便调试(线程在哪个对象上阻塞了)private static void setBlocker(Thread t, Object arg) {
        // Even though volatile, hotspot doesn‘t need a write barrier here.
        UNSAFE.putObject(t, parkBlockerOffset, arg);
    }
public static Object getBlocker(Thread t) {
        if (t == null)
            throw new NullPointerException();
        return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
    }

感谢:https://www.cnblogs.com/leesf456/p/5347293.html

原文地址:https://www.cnblogs.com/FondWang/p/12112251.html

时间: 2024-10-10 10:01:55

JUC之LockSupport构建同步组件的基本工具的相关文章

Java显式锁学习总结之二:使用AbstractQueuedSynchronizer构建同步组件

Jdk1.5中包含了并发大神Doug Lea写的并发工具包java.util.concurrent,这个工具包中包含了显示锁和其他的实用同步组件.Doug Lea在构建锁和组件的时候,大多是以队列同步器(AbstractQueuedSynchronizer)为基础的,因此AbstractQueuedSynchronizer可以看作是并发包的基础框架.因此掌握了AbstractQueuedSynchronizer的实现原理,也就掌握了大多数并发组件的实现原理. AbstractQueuedSync

AQS 原理以及 AQS 同步组件总结

1 AQS 简单介绍 AQS 的全称为(AbstractQueuedSynchronizer),这个类在 java.util.concurrent.locks 包下面. AQS 是一个用来构建锁和同步器的框架,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的 ReentrantLock,Semaphore,其他的诸如 ReentrantReadWriteLock,SynchronousQueue,FutureTask 等等皆是基于 AQS 的.当然,我们自己也能利用 AQ

java实现自定义同步组件的过程

实现同步组件twinsLock:可以允许两个线程同时获取到锁,多出的其它线程将被阻塞. 以下是自定义的同步组件类,一般我们将自定义同步器Sync定义为同步组件TwinsLock的静态内部类. 实现同步器需要继承AbstractQueuedSynchronizer并覆盖相应的方法. package com.lock; import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.AbstractQueuedSynchr

如何实现自定义同步组件

package com.chen; import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.AbstractQueuedSynchronizer;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock; /** * desc:自定义同步组件,只允许最多n个线程同时访问 * // 1.共享访问 * //

AQS的几个同步组件

AQS的几个同步组件 CountDownLatch 一个线程或多个线程一直等待,直到其他线程执行的操作完成才继续执行. 应用:并行计算. 计数器是不能重计的,计数值不能往上加,类似操作只有一次. 使用时首先new一个countDownLatch,构造方法中放入计数,然后在一个线程中调用await方法,这个线程就会进入等待状态,其他线程中调用countDown方法减少这个计数,直至降到0时,await方法阻塞的线程会被唤醒继续工作,为保证顺利执行,countDown方法一定要放在finally块里

前端开发构建:13 种热门工具的选型参考

前言 在前端项目的规模和复杂性不断提升的情况下,各类构建思想和相应工具层出不穷.本文竭己所能对比了当下13个构建工具,包括 Browserify. Webpack. Rollup. Grunt. Gulp和 Yeoman6个广为流行的工具, FIS. Athena. WeFlow和 Cooking等4个国产工具,以及三大框架: React, Vue和 Angular的官方脚手架.希望能在项目初期的构建工具选型上为大家提供些参考. 全览 构建工具可以分为三类:模块化打包类.任务流构建类和集合型工具

Kivy a to Z -- 一个简单的通过adb同步Android系统文件的工具

来兴趣时写了些Kivy的代码,调试却总感觉不是很方便.直接打包到public.mp3的方式太繁锁,用文件共享的软件又发现没有一个好用的, 用samba filesharing本来也只是慢,但是更新的版本之后就一直提示说wifi没有tethering,意思是wifi热点没有打开,但是打开了还是提示没有tethering. 找了个叫什么卓*力的文件管理器,下载了samba插件后输入用户名和密码死活不对,被搞得实在恼火,花了点时间写了个通过adb同步安卓文件的工具,用着也挺爽的. 事件为什么总是要搞得

已禁用对分布式事务管理器(MSDTC)的网络访问。请使用组件服务管理工具启用 DTC 以便在 MSDTC 安全配置中进行网络访问。

已禁用对分布式事务管理器(MSDTC)的网络访问.请使用组件服务管理工具启用 DTC 以便在 MSDTC 安全配置中进行网络访问. 此错误好像只有sql server2005才有,2008数据库以后版本就没有此错误了. 与基础事务管理器的通信失败. .net 代码里 写事务代码 如: using System.Transactions; using (TransactionScope ts = new TransactionScope()) { } ts.Complete(); DTC登陆账户为

java多线程10.构建同步工具

创建状态依赖类的最简单方法通常是在类库中现有状态依赖类的基础上进行构造.如果类库中没有提供你需要的功能,可以使用java语言和类库提供的底层机制来构造自己的同步机制,包括内置的条件队列.显示地Condition对象以及AbstractQueuedSynchronizer框架. 在单线程程序中调用方法时,如果基于某个状态的前提条件未得到满足,那么这个条件永远无法成真.而在并发程序中,基于状态的条件可能会由于其他线程的操作而改变.可阻塞的状态依赖操作 acquire lock on object s