​LockSupport源码解析

时间:2022-07-23
本文章向大家介绍​LockSupport源码解析,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

JUC包中进行线程阻塞和线程唤醒的时候使用了一个工具类,这个类就是LockSupport,在AQS中对线程的阻塞和唤醒就是依靠此类完成的,那么LockSupport是如何实现的里?确实当时我们只是大概知道了基本的过程,并没有进行深入。因为在AQS中我们知道有一个线程链表,链表的每个节点就是Node。

为了让我们的思维更加有理有据,所以我觉得又到了整理一下思维的时候。我们知道多线程代码在跑的时候遇到独占锁的时候才会加入我们上边说的Node,也才会进行单核CPU运行。而AQS链表也只是取第一个节点进行运行。这么看来这个队列本来就具有一种特点就是可以阻塞了。那么为啥还要用一个工具类?比如说我这个链表第一个运行完毕了,然后要唤醒其他线程。我应该用for循环去唤醒吗?看来好像有点问题,我们无法保证没有人抢跑。有鉴于此还是看一下源码,来看看为啥用的这么一个工具类。以及这个工具类干了些什么。

首先引入眼帘的是LockSupport的静态代码块,发现使用到了Unsafe的内存操作。如果用内存操作那么肯定要用到偏移量什么了,这在代码均有体现。然后通过Unsafe类来Thread类中的属性偏移量,那么记录这些偏移量的意义是什么?为什么要这么做?怀着疑问继续前进。但是发现在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 = sun.misc.Unsafe.getUnsafe();
            Class<?> tk = Thread.class;
            parkBlockerOffset = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("parkBlocker"));
            SEED = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomSeed"));
            PROBE = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomProbe"));
            SECONDARY = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomSecondarySeed"));
        } catch (Exception ex) { throw new Error(ex); }
    }

我们在加锁时候一般调用的是lock,而lock最后也是采用的LockSupport提供的park方法。park就是让一个线程阻塞的方法。因为LockSupport是一个工具方法,所以我们在AQS中看看是如何使用这个工具的。

我们看到AQS在调用的时候将this传入了,this就是AQS本身,AQS包含了他多的类。如果这样的话,LockSupport就成为皇家专属产品,我们这种屌丝看来也用不到了。。zz,是这样么?咋还是不要轻易下结论。

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

在park方法中,首先获取当前线程,然后和AQS做为参数调用了setBlocker方法。这里好像用了两次哦,,,感觉要复杂了。。。

     private static void setBlocker(Thread t, Object arg) {
        // Even though volatile, hotspot doesn't need a write barrier here.
        UNSAFE.putObject(t, parkBlockerOffset, arg);
    }

在setBlocker方法中也是直接调的Unsafe的方法,看来是完全不给咋理解的机会啊,那么没办法,咋只能猜啊。因为这个offset是Thread类中的一个变量。而这个变量是Volatile修饰的而Unsafe一般都是操作这中修饰的属性的,况且这个offset就是也是从thread类中获取的,那么这里应该就是往当前线程里设置AQS。那么设置了AQS之后,有很么用呐?通过搜索之后发现这里不一定要设置成AQS,就是LockSupport并不是皇家专用,这里的AQS其实就是线程阻塞的调用者。而Unsafe.park其实也就是让线程停下来。那么在线程停下来之后为啥要设置调用者为null?我的感觉是用不到了这个调用者了那么就不要了。。多么简单的解释。其实我不太懂,但感觉是这样的,只能说park方法里可能要用这个,然后用完之后就不用了。

代码看到这里的时候就觉得这个类已经没啥了。就是让线程睡眠而已。

那么我们继续看一下unpark这个方法。发现也是直接操作的内存进行唤醒线程。

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

总结一下LockSupport

第一为是阻塞线程和唤醒线程,同时LockSupport提供了多种阻塞的方法。

第二在阻塞线程的时候将调用者设置到被阻塞线程的Blocker属性中,用于某种日志操纵。