【java基础】线程笔记——LockSupport

LockSupport

这里写图片描述

LocalSupport类特性

  • 不可实例化
private LockSupport() {} // Cannot be instantiated.
  • LockSupport的方法都是静态方法

  • 私有变量

    private static final sun.misc.Unsafe UNSAFE;
    private static final long parkBlockerOffset;

私有变量:unsafe

  1. sun.misc.Unsafe可以直接操控内存。被JDK广泛用于自己的包中,如java.nio和java.util.concurrent
  2. API十分不安全、不轻便、而且不稳定。不建议在生产环境中使用
  3. 这个不安全的类提供了一个观察HotSpot JVM内部结构并且可以对其进行修改。有时它可以被用来在不适用C++调试的情况下学习虚拟机内部结构,有时也可以被拿来做性能监控和开发工具

私有变量: parkBlockerOffset

  • 被LockSupport的setBlocker和getBlocker调用
  • 这个对象是用来记录线程被阻塞时被谁阻塞的,用于线程监控和分析工具来定位原因

LockSupport.java

/**
 * 通过反射机制获取Thread类的parkBlocker字段对象。然后通过   
 * sun.misc.Unsafe对象的objectFieldOffset方法获取到
 * parkBlocker在内存里的偏移量
 */
parkBlockerOffset = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("parkBlocker"));

Thread.java

    /**
     * The argument supplied to the current call to
     * java.util.concurrent.locks.LockSupport.park.
     * Set by (private) java.util.concurrent.locks.LockSupport.setBlocker
     * Accessed using java.util.concurrent.locks.LockSupport.getBlocker
     */
    volatile Object parkBlocker;

JVM的实现可以自由选择如何实现Java对象的“布局”,也就是在内存里Java对象的各个部分放在哪里,包括对象的实例字段和一些元数据等。
sun.misc.Unsafe里关于对象字段访问的方法把对象布局抽象出来,它提供了objectFieldOffset()方法用于获取某个字段相对 Java对象的“起始地址”的偏移量,也提供了getInt、getLong、getObject之类的方法可以使用前面获取的偏移量来访问某个Java 对象的某个字段

为什么要用偏移量来获取对象?不直接调用get/set方法?

parkBlocker就是在线程处于阻塞的情况下才会被赋值。线程都已经阻塞了,如果不通过这种内存的方法,而是直接调用线程内的方法,线程是不会回应调用的


LockSupport Method

  • LockSupport中有且只有一个私有方法
    • 对给定线程t的parkBlocker赋值。为了防止这个parkBlocker被误用,该方法是不对外公开的
private static void setBlocker(Thread t, Object arg) {
        // Even though volatile, hotspot doesn't need a write barrier here.
        UNSAFE.putObject(t, parkBlockerOffset, arg);
}

参数

  • Thread t 需要被赋值Blocker的线程
  • Object arg 具体的Blocker对象

  • 从线程t中获取它的parkBlocker对象,即返回的是阻塞线程t的Blocker对象
public static Object getBlocker(Thread t) {  
    if (t == null)  
        throw new NullPointerException();  
    return unsafe.getObjectVolatile(t, parkBlockerOffset);  
}  

以park开头的方法,用于阻塞线程

带blocker参数的park方法

public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, 0L);
        setBlocker(t, null);
}

public static void parkNanos(Object blocker, long nanos) {
        if (nanos > 0) {
            Thread t = Thread.currentThread();
            setBlocker(t, blocker);
            UNSAFE.park(false, nanos);
            setBlocker(t, null);
        }
}

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

参数:

  • Object blocker:用于记录到线程中的parkBlocker对象。
  • nanos:在nanos时间后线程自动恢复挂起
  • deadline:在deadline时刻线程自动(这个毫秒其实就是自1970年1月1日0时起的毫秒数)

不带blocker参数的park方法

public static void park() {
        UNSAFE.park(false, 0L);
}

public static void parkNanos(long nanos) {
        if (nanos > 0)
            UNSAFE.park(false, nanos);
}

public static void parkUntil(long deadline) {
        UNSAFE.park(true, deadline);
}
  • 没有做parkBlocker的赋值操作

以unpark开头的方法,用于解除阻塞

public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
}

park与unpark命令是成对出现的。unpark必须要在park命令后执行。但是线程的恢复并不一定要用unpark, 因为park的时间参数,有些情况下线程会自己恢复

源码参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值