LockSupport工具类介绍

一、LockSupport 类功功能介绍

       LockSupport是用于juc包下用于操作线程阻塞的工具类,可以看到底层都是通过CAS来支持。

       AQS中实现线程挂起的方法,就是park,对应唤醒就是unpark。

       LockSupport提供的是一个许可,如果存在许可,线程在调用park的时候,会立马返回,

      此时许可也会被消费掉,如果没有许可,则会阻塞。调用unpark的时候,如果许可本身

       不可用,则会使得许可可用。

       许可只有一个,不能累加

二、LockSupport 类方法介绍

       

public class LockSupport {
  
    private LockSupport() {} // Cannot be instantiated.

    /**
     * 设置线程阻塞的对象信息(即记录线程t 阻塞的原因 ),用于debug、jstack
     */
    private static void setBlocker(Thread t, Object arg) {
        //   即使hotspot是不稳定的,它也不需要写屏障。
        UNSAFE.putObject(t, parkBlockerOffset, arg);//去除CAS的volatile优化
    }

    /**
     * 唤醒当前阻塞的线程
     */
    public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }

    /**
     * 带有提示对象的阻塞
     * 用于挂起(阻塞)当前线程,如果许可可用则调用立即返回,并消费掉许可,
     * 若许可不可用则会阻塞。
     * 如果许可是可用的,那么它被消耗并且调用立即返回;否则,当前线程会因为线程调度的目的而被禁用,
     * 并处于休眠状态,直到发生以下三种情况之一才会恢复:
     *   1)其他一些线程以当前线程为参数调用unpark;
     *   2)在其他线程中 中断当前线程
     *   3)调用虚假地(也就是说,毫无理由地)返回,即发生了不可预料的事情
     *无论是什么情况返回,park方法本身都不会告知调用方返回的原因,所以调用的时候一般都会去判断返回的场景,根据场景做不同的处理
     *
     * park英文意思为停车。我们如果把Thread看成一辆车的话,park就是让车停下(阻塞线程),unpark就是让车启动然后跑起来(唤醒线程)
     *
     * park调用时,判断许可是否为true,如果是true,则继续往下执行;如果是false,则等待,直到许可为true
     * unpark调用时,如果当前线程还未进入park,则许可为true
     *
     * 参数:
     *    blocker-- 负责该线程阻塞的同步对象(即 锁 对象),
     */
    public static void park(Object blocker) {
        //获取当前线程
        Thread t = Thread.currentThread();
        //记录线程阻塞的原因,底层就是 unsafe.putObject(),就是把对象 blocker 存储起来
        setBlocker(t, blocker);
        //执行park
        UNSAFE.park(false, 0L);
        //线程恢复后去掉阻塞原因
        setBlocker(t, null);
    }



    /**
     * 带超时时间(绝对时间)和阻塞原因的阻塞当前线程
     * 用于挂起(阻塞/暂停)当前线程,该方法相对于上面的park方法,多了超时时间 nanos,表示若许可可用,则调用立即返回,并消费掉
     * 许可,否则会把当前线程挂起,并等待超时时间 nanos,当过了 nanos 后若还没有可用许可(或还没有调用unPark唤醒当前线程),
     * 则会把当前线程自动唤醒;
     * 恢复的条件为
     *     1:线程调用了unpark;
     *     2:其它线程中断了线程;
     *     3:发生了不可预料的事情;
     *     4:过期时间到了
     *
     */
    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);
        }
    }


    /**
     * 带有超时时间(相对时间)和阻塞对象的线程阻塞操作
     * 用于挂起当前线程直到某个时刻(时间点) deadline
     */
    public static void parkUntil(Object blocker, long deadline) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(true, deadline);
        setBlocker(t, null);
    }

    /**
     * 获取阻塞原因
     */
    public static Object getBlocker(Thread t) {
        if (t == null)
            throw new NullPointerException();
        return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
    }

    /**
     * 阻塞当前线程
     */
    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);
    }

    /**
     * 
     */
    static final int nextSecondarySeed() {
        int r;
        Thread t = Thread.currentThread();
        if ((r = UNSAFE.getInt(t, SECONDARY)) != 0) {
            r ^= r << 13;   // xorshift
            r ^= r >>> 17;
            r ^= r << 5;
        }
        else if ((r = java.util.concurrent.ThreadLocalRandom.current().nextInt()) == 0)
            r = 1; // avoid zero
        UNSAFE.putInt(t, SECONDARY, r);
        return r;
    }

   
    private static final sun.misc.Unsafe 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();
            //通过Thread 的 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); }
    }

}

三、park/unpark 与 wait/notify 的区别?

       1)park--unPark 类似于对象锁中的 wait-notify/notifyAll,也是用来阻塞/唤醒 线程,

            但 wait--notify 只能先wait,后 notify,使用不当的话可能会引起死锁的问题,

            且notify 唤醒的线程是随机的;

      2)park--unpark 可以先执行 unpark(唤醒),后执行 park(阻塞),所以不回引起

           死锁的问题,unpark 可以唤醒指定的线程;

      3)wait -- notify 的调用需要依赖对象,但 park--unPark 不需要依赖对象,只需要

           通过 LockSupport 类来调用,使用比较灵活;

      4)park不会抛出InterruptedException异常,所以需要在park之后自行判断中断状态,

            然后做额外的处理

      

       

       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值