03.JUC 锁 - LockSupport

基本概念

LockSupport 可以用来实现线程的阻塞/唤醒。

每个使用 LockSupport 的线程都会与一个许可关联:

  • 若该许可有效,则线程可以继续执行。
  • 若该许可无效,则线程进入阻塞,等待许可证生效后再继续执行。

关于许可,可类比为停车场的车位:

  • 若车位是被占用(即不可用),想要停车(park)则必须等待车位空余出来。
  • 通过 unpark 把车位空出来(即可用),则就可以直接停车。

关于 LockSupport ,它具有以下特点:

  • 许可默认是失效的(不可用)
// 例子①:无法打印,因为许可失效,线程进入阻塞
LockSupport.park();
System.out.println(111);

// 例子②:先使许可生效,线程继续执行,打印输出
LockSupport.unpark(Thread.currentThread());
LockSupport.park();
System.out.println(111);
  • 无法重入
// 第二次调用 park 时,线程进入阻塞。
LockSupport.unpark(Thread.currentThread());
LockSupport.park();
System.out.println(111);
LockSupport.park();
System.out.println(222);

原理分析

源码来自 JDK 1.7

1.内部构造

LockSupport 类的构造函数被私有化了,说明它无法被实例化。但是它的所有操作都是静态的,可直接调用。

private LockSupport() {} 

再来看下它的成员变量:

private static final Unsafe unsafe = Unsafe.getUnsafe();  
private static final long parkBlockerOffset;  
  • unsafe:该类用于可以直接操控内存,被 JDK 广泛用于自己的包中,如 java.nio 和java.util.concurrent。但是丝毫不建议在生产环境中使用这个类。因为这个 API 十分不安全、不轻便、而且不稳定。这个不安全的类提供了一个观察 HotSpot JVM 内部结构并且可以对其进行修改。有时它可以被用来在不适用C++调试的情况下学习虚拟机内部结构,有时也可以被拿来做性能监控和开发工具。

  • parkBlockerOffset:用于记录线程是被谁阻塞的。可以通过LockSupport 的 getBlocker 获取到阻塞的对象。主要用于监控和分析线程用的。

2.park 操作

该操作表示禁用当前线程,除非许可证可用。好比停车的动作,除非车位空闲出来,否则想要停车只能等待。该类支持三种不同的 park 操作:

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

// 设置阻塞对象
private static void setBlocker(Thread t, Object arg) {
    unsafe.putObject(t, parkBlockerOffset, arg);
}

// ②parkNanos
public static void parkNanos(long nanos) {...}
public static void parkNanos(Object blocker, long nanos) {...}

// ③parkUntil
public static void parkUntil(long deadline) {...}
public static void parkUntil(Object blocker, long deadline) {...}

存在以下 5 种情况,线程的状态会被改变:

  • 其他线程将当前线程作用目标调用 unpark 方法。
// 创建线程并启动
Thread t1= new Thread(new Runnable() {
    private int count =0;
    public void run() {
        // 默认许可不可用,线程进入阻塞。无法输出。
        LockSupport.park();
        System.out.println(111);
    }
});
t1.start();

try {
    // 主线程休眠一段时间,确保 t1 的动作先完成 
    Thread.currentThread().sleep(2000);
    // 主线程将 t1 作为目标调用 unpark 方法,t1 被唤醒,打印输出
    LockSupport.unpark(t1);
} catch (InterruptedException e) {
    e.printStackTrace();
}
  • 其他线程通过 interrupt 方法中断当前线程。
// 启动线程,代码同上
Thread t1= ...
t1.start();

try {
    Thread.currentThread().sleep(2000);
    // 主线程修改中断标志位, t1 线程中断
    t1.interrupt();
} catch (InterruptedException e) {
    e.printStackTrace();
}
  • 调用不合逻辑地(即毫无理由地)返回

  • 经过一段等待时间(针对 parkNanos)

  • 到达截至时间(针对 parkUntil)

3.unPark 操作

该操作表示若线程的许可尚不可用,则使其可用。好比将车位的车挪出去,那么停车动作(park)就可以继续。

public static void unpark(Thread thread) {
    if (thread != null){
        unsafe.unpark(thread);
    }
}
    原文作者:JUC
    原文地址: https://blog.csdn.net/u012420654/article/details/55044605
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞