源码解读(三):Java中的LockSupport的使用

1、LockSupport方法介绍

在Java多线程中,当需要阻塞或唤醒一个线程时,我们可以使用LockSupport工具类来完成相应的功能。而LockSupport也成为构成同步器的重要工具。AbstractQueuedSynchronizer就是通过LockSupport来实现线程阻塞和唤醒的。
下面我们分别介绍一下它的主要方法。

方法作用
void unpark(Thread thread)唤醒处于阻塞状态的线程
void park()阻塞当前线程,调用unpark(Thread thread) 唤醒该线程
void parkNanos(long nanos)阻塞当前线程,阻塞时间不超过nanos纳秒
void parkUntil(long deadline)阻塞当前线程,直到deadline时间点,deadline时间点以System.currentTimeMillis()为参考
void park(Object blocker)与park()相同, blocker标识等待对象
void parkNanos(Object blocker, long nanos)与parkNanos(long nanos)相同, blocker标识等待对象
void parkUntil(Object blocker, long deadline)与parkUntil(long deadline) 相同, blocker标识等待对象

对比park() 与park(Object blocker) 的区别

下面一段代码,分别调用了park()和park(Object blocker)方法,使其对应线程阻塞,随后我们查看其线程阻塞情况。

public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                LockSupport.park();
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                LockSupport.park(new Object());
            }
        });
        t1.start();
        t2.start();
    }

这里利用jps命令先找出执行代码对应pid,然后通过jstack命令打出thread dump信息。
找出对应线程,打印dumo信息
随后我们对比两个阻塞方法之间的差别,使用红色矩形框起来以方便查看。很明显的缺少了一行 - parking to wait for 标识等待的对象。这个对象主要用于问题排查和系统监控。
对比差别

parkUntil(long deadline) 方法的时间点在哪里

刚开始我也不是很清楚这个时间点在哪里,但看到时间点的类型为long,就想到System.currentTimeMillis()的时间类型也为long,于是就出现下面代码了。

public static void main(String[] args) {
        long start = System.currentTimeMillis();
        System.out.println("start time:" + start);
        // 以System.currentTimeMillis()的时间点为开始时间
        LockSupport.parkUntil(System.currentTimeMillis() + 1000);
        System.out.println("park time:" + (System.currentTimeMillis() - start) + " ms");
    }

看到运行结果后,印证了我的猜测,时间点是以System.currentTimeMillis()为基线的。可能我表达不够准确,最好还是参考代码帮助理解。
运行结果

2、LockSupport源码及注释

UNSAFE.park(boolean var1, long var2),该方法有两个参数。
第一个参数是是否是绝对时间,第二个参数是等待时间值。如果isAbsolute是true则会实现ms定时。如果isAbsolute是false则会实现ns定时。

public class LockSupport {
	// 私有化构造函数,禁止创建实例
    private LockSupport() {} 
    // 为线程设置阻塞对象
    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 void unpark(Thread thread) {
    	// 调用UNSAFE类
        if (thread != null)
            UNSAFE.unpark(thread);
    }

    /**
     * 在blocker对象上进行阻塞
     * @since 1.6
     */
    public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        // 为当前线程设置阻塞对象
        setBlocker(t, blocker);
        // 阻塞
        UNSAFE.park(false, 0L);
        // 阻塞被唤醒,释放线程的阻塞对象。
        setBlocker(t, null);
    }

    /**
     * @since 1.6
     */
    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);
        }
    }

    /**
     * @since 1.6
     */
    public static void parkUntil(Object blocker, long deadline) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        // 阻塞,并设置绝对超时时间
        UNSAFE.park(true, deadline);
        setBlocker(t, null);
    }

    /**
     * 
     * @since 1.6
     */
    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);
    }

    /**
     * 该方法只在StampedLock类中用到,暂时不作讨论
     */
    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;
    }

    // Hotspot implementation via intrinsics API
    private static final sun.misc.Unsafe UNSAFE;
    // 获取属性在对应类中的Offset位置
    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); }
    }
}

3、关于sun.misc.Unsafe类型

我们在阅读Java并发包中多个类的源码时都遇到这个类的使用,类中大多都是native方法,主要用于执行低级别、不安全的方法。我会和大家一起来理解方法的作用,不会深入讨论Unsafe方法的实现,若读者感兴趣可以自行寻找相关文章。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值