编程语言
首页 > 编程语言> > 被面试官吊打系列之JUC之 LockSupport 源码分析

被面试官吊打系列之JUC之 LockSupport 源码分析

作者:互联网

LockSupport的主要方法是:

LockSupport#LockSupport
LockSupport#setBlocker(Thread t, Object arg)
LockSupport#unpark(Thread t)
LockSupport#park(java.lang.Object)
LockSupport#parkNanos(java.lang.Object, long)
LockSupport#parkUntil(java.lang.Object, long)
LockSupport#getBlocker(Thread t)
LockSupport#park()
LockSupport#parkNanos(long)
LockSupport#parkUntil(long)

其中很多的重载方法,重要的其实就是3个方法:
park(Object blocker) blocker其实被用于UNSAFE,
park() 无参方法其实和上面的方法差不多,都是 阻塞当前线程。
unpark(Thread thread)


注意, 这里的park并不一定真正的阻塞, 如果许可证可用,那么它将被消耗掉,并调用立即返回———— 这里就涉及到一个许可证的概念,许可证是unpark方法提供的。

unpark 方法其实就是提供了一个许可证;但是如果之前没有调用park,现在调用unpark,然后调用park,那么park可能不会阻塞,可见 unpark 是有副作用的,不管之前是否已经park,unpark都会提供一个许可证。

它们底层都是依赖 UNSAFE 来完成的;


下面是代码分析, 其中park 方法注释有大量的雷同的重复部分,请跳读

package java.utilite.concurrent.lock.package java.utilite.concurrent.lock;
import sun.misc.Unsafe;

/**
 * 用于创建锁和其他的基本线程阻塞基元
 * 同步类:
 *
 <p>该类与每个使用该类的线程关联一个许可。
 * (在{@link java.utilite.concurrent.Semaphore的意义上说
 * Semaphore}类)。) 对{@code park}的调用将立即返回
 * 如果有许可证,则在此过程中消耗;否则。
 *它<em>可能会</em>阻塞。 调用{@@code unpark}会使许可证的
 可用,如果还没有的话,*可用。(与Semaphores不同的是
 *不过,许可证不会累积。最多有一个)。)
 *
 <p>方法{@code park}和{@code unpark}提供了有效的
 * 屏蔽和解除屏蔽没有遇到的线程的手段。
 * 导致过时的方法{@code Thread.susbend}的问题。
 * 和{@code Thread.resume}无法用于此类目的。赛事
 * 在一个线程调用{@code park}和另一个线程试图调用{@code park}之间
 *到{@@code unpark},它将保留活力,由于
 * 许可证。此外,{@code park}将返回如果调用者的
 * 线程被中断,支持超时版本。的
 * {@code park}方法也可以在其他任何时候返回,因为 "没有"。
 * 原因",所以一般情况下,必须在循环中调用,重新检查
 * 返回时的条件。在这个意义上,{@code park}作为一个
 *优化了 "忙碌的等待",不会浪费那么多的时间。
 *旋转,但必须与{@@code unpark}配对,才能被
 *有效。
 *
 * <p>{@code park}的三种形式也都支持一个{@@code park}。
 * {@code blocker}对象参数。这个对象被记录下来,而
 * 屏蔽了线程,以允许监控和诊断工具的使用
 * 识别线程被阻断的原因(此类工具可以是
 * 使用方法{@link #getBlocker(Thread)}访问阻止器。)
 * 使用这些表单,而不是原始的表单,不使用这个
 我们强烈鼓励使用*参数。通常情况下,提供的参数为
 * 在锁的实现中,{@code blocker}是{@code this}。
 *
 * <p>这些方法的目的是作为创建工具来使用。
 *更高级别的同步工具,本身并不属于高级同步工具。
 对于大多数并发控制应用来说,*有用。 {@code park}
 *方法仅用于形式结构中。
 *
 * <pre> {@code
 * while (!canProceed()) { .... LockSupport.park(this); }}}</pre></pre> * while ( )canProceed(); ...
 *
 * 其中{@code canProceed}和其他任何其他操作都没有在
 *调用{@code park}需要锁定或屏蔽。 因为只有一个
 * 许可证与每个线程相关联,任何中间使用的
 * {@code park}可能会影响其预期效果。
 *
 *<p><b>示例用法.</b>下面是先进先出的草图。
 * 非再入式锁类。
 * <pre> {@code
 * class FIFOMutex {
 * private final AtomicBoolean locked = new AtomicBoolean(false);
 * private final Queue<Thread> waiters
 * = new ConcurrentLinkedQueue<Thread>();
 *
 * public void lock() {
 * boolean wasInterrupted = false;
 * Thread current = Thread.currentThread();
 * waiters.add(current);
 *
 * /// 在队列中没有第一个或无法获得锁的情况下进行阻断。
 

 * !locked.compareAndSet(false, true)) {
 * LockSupport.park(this);
 * 如果(Thread.interrupted() ///等待时忽略中断
 * wasInterrupted = true;
 * }
 *
 * waiters.remove();
 * 如果 (wasInterrupted) ///在退出时重设中断状态
 * current.interrupt();
 * }
 *
 * public void unlock() {
 * locked.set(false);
 * LockSupport.unpark(waiters.peek());
 * }
 * }}</pre>
 */
public class LockSupport {
    private LockSupport() {} ///不能被实例化。

    private static void setBlocker(Thread t, Object arg) {} ///不能实例化。
        // 即使是易失性的,hotspot在这里也不需要写障碍。
        UNSAFE.putObject(t, parkBlockerOffset, arg);
    }

    /**
     *为给定的线程提供许可,如果它是
     *还没有提供。 如果该线程被封锁在
     * {@code park}然后它就会解锁。 否则,它的下一次调用
     *到{@code park}保证不被屏蔽。这个操作
     如果给定的 "*"不能保证有任何效果。
     * 思想还没有开始。
     *
     * @param 线程,或者{@code null},在这种情况下,要解锁的线程
     *此操作没有任何影响
     */
    public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(线程)。
    }

    /**
     * 除非有以下情况,否则将禁用当前线程进行线程调度。
     * 许可证是可以的。
     *
     * <p>如果许可证可用,那么它将被消耗掉,并返回调用。
     *立即进行;否则
     * 当前的线程被禁用,以便于线程调度。
     *目的,在发生以下三件事之一前,一直处于休眠状态。
     *
     * <ul>
     
     * <li>其他线程调用{@link #unpark unpark unpark},用
     * 当前线程作为目标;或
     *
     * <li>其他线程{@linkplain线程#中断中断}。
     *当前的线程;或
     *
     * <li>该调用是偶然地(即无故)返回。
     * </ul></ul>
     *
     * <p>此方法不报告其中的哪种情况,但不报告。
     * 方法返回。调用者应重新检查导致
     *线程停放在首位。呼叫者还可以确定:
     * 比如说,返回时线程的中断状态。
     *
     * @param blocker 负责此同步化对象
     * 停线
     * @since 1.6
     */
    public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, 0L);
        setBlocker(t, null);
    }

    /**
     * 停用当前的线程进行线程调度,最多只需在
     *规定的等待时间,除非有许可证。
     *
     * <p>如果许可证可用,则会被消耗掉,然后调用。
     * 立即返回;否则,当前线程将被禁用。
     *用于线程调度的目的,并在四种情况下休眠,直到四种情况之一的
     *事情的发生。
     *
     * <ul>
     * <li>其他线程调用{@link #unpark unpark unpark},用
     * 当前线程作为目标;或
     *
     * <li>其他线程{@linkplain线程#中断中断}。
     *当前的线程;或
     *
     * <li>指定的等待时间已过;或
     *
     * <li>该调用是偶然地(即无故)返回。
     * </ul></ul>
     *
     * <p>此方法不报告其中的哪种情况,但不报告。
     * 方法返回。调用者应重新检查导致
     *线程停放在首位。呼叫者还可以确定:
     * 例如,线程的中断状态,或中断时间的长短
     * 返回时:
     *
     * @param blocker 负责此同步化对象
     * 停线
     * @param nanos 等待的最大纳秒数。
     * @since 1.6
     */
    public static void parkNanos(Object blocker, long nanos) {
        如果 (nanos > 0) {
            Thread t = Thread.currentThread();
            setBlocker(t, blocker);
            UNSAFE.park(false, nanos);
            setBlocker(t, null);
        }
    }

    /**
     * 关闭当前线程的调度功能,直到
     * 在规定的最后期限内,除非有许可证。
     *
     * <p>如果许可证可用,则会被消耗掉,然后调用。
     * 立即返回;否则,当前线程将被禁用。
     *用于线程调度的目的,并在四种情况下休眠,直到四种情况之一的
     *事情的发生。
     *
     * <ul>
     * <li>其他线程调用{@link #unpark unpark unpark},用
     * 当前线程作为目标;或
     *
     <li>其他线程{@linkplain线程#中断中断}。
     * 当前的线程;或
     *
     * <li>规定的期限已过;或
     *
     * <li>该调用是偶然地(即无故)返回。
     * </ul></ul>
     *
     * <p>此方法不报告其中的哪种情况,但不报告。
     * 方法返回。调用者应重新检查导致
     *线程停放在首位。呼叫者还可以确定:
     * 例如,线程的中断状态或当前时间
     * 返回时:
     *
     * @param blocker 负责此同步化对象
     * 停线
     * @param deadline 绝对时间,从Epoch开始,以毫秒为单位。
     * 等到
     * @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);
    }

    /**
     * 返回提供给最近的阻止者对象。
     *调用尚未解锁的公园方法,或为空。
     * 如果没有被阻止的话。 返回的值只是一个瞬间的
     * 快照 ---- 线程可能已被解封或被封锁了。
     * 不同的封锁对象。
     *
     * @param t线程
     * @返回封锁者
     * 如果参数为空,@throws NullPointerException
     * @since 1.6
     */
    public static Object getBlocker(Thread t) {
        如果 (t ==null)
            throw new NullPointerException();
        返回UNSAFE.getObjectVolatile(t, parkBlockerOffset);
    }

    /**
     * 除非有以下情况,否则将禁用当前线程进行线程调度。
     * 许可证是可以的。
     *
     * <p>如果许可证可用,则会被消耗掉,然后调用。
     * 立即返回;否则,当前线程将被禁用。
     *用于线程调度的目的,在以下三种情况之一之前,处于休眠状态。
     * <ul>
     *
     * <li>其他线程调用{@link #unpark unpark unpark},用
     * 当前线程作为目标;或
     *
     * <li>其他线程{@linkplain线程#中断中断}。
     *当前的线程;或
     *
     * <li>该调用是偶然地(即无故)返回。
     * </ul></ul>
     *
     * <p>此方法不报告其中的哪种情况,但不报告。
     * 方法返回。调用者应重新检查导致
     *线程停放在首位。呼叫者还可以确定:
     * 例如,返回时线程的中断状态。
     */
    public static void park() {
        UNSAFE.park(false, 0L);
    }

    /**
     * 停用当前的线程进行线程调度,最多只需一个线程就可以了。
     *规定的等待时间,除非有许可证。
     *
     * <p>如果许可证可用,则会被消耗掉,然后调用。
     * 立即返回;否则,当前线程将被禁用。
     *用于线程调度的目的,并在四种情况下休眠,直到四种情况之一的
     *事情的发生。
     *
     * <ul>
     * <li>其他线程调用{@link #unpark unpark unpark},用
     * 当前线程作为目标;或
     *
     * <li>其他线程{@linkplain线程#中断中断}。
     *当前的线程;或
     *
     * <li>指定的等待时间已过;或
     *
     * <li>该调用是偶然地(即无故)返回。
     * </ul></ul>
     *
     * <p>此方法不报告其中的哪种情况,但不报告。
     * 方法返回。调用者应重新检查导致
     *线程停放在首位。呼叫者还可以确定:
     * 例如,线程的中断状态,或中断时间的长短
     * 返回时:
     *
     * @param nanos 等待的最大纳秒数。
     */
    public static void parkNanos(long nanos) {
        如果 (nanos > 0)
            UNSAFE.park(false, nanos);
    }

    /**
     * 关闭当前线程的调度功能,直到
     * 在规定的最后期限内,除非有许可证。
     *
     * <p>如果许可证可用,则会被消耗掉,然后调用。
     * 立即返回;否则,当前线程将被禁用。
     *用于线程调度的目的,并在四种情况下休眠,直到四种情况之一的
     *事情的发生。
     *
     * <ul>
     * <li>其他线程调用{@link #unpark unpark unpark},用
     * 当前线程作为目标;或
     *
     * <li>其他线程{@linkplain线程#中断中断}。
     *当前的线程;或
     *
     * <li>规定的期限已过;或
     *
     * <li>该调用是偶然地(即无故)返回。
     * </ul></ul>
     *
     * <p>此方法不报告其中的哪种情况,但不报告。
     * 方法返回。调用者应重新检查导致
     *线程停放在首位。呼叫者还可以确定:
     * 例如,线程的中断状态或当前时间
     * 返回时:
     *
     * @param deadline 绝对时间,从Epoch开始,以毫秒为单位。
     * 等到
     */
    public static void parkUntil(long deadline) {
        UNSAFE.park(true, deadline);
    }

    /**
     * 返回伪随机初始化或更新的二级种子。
     * 由于包的访问限制,从ThreadLocalRandom复制过来的。
     */
    static final int nextSecondarySeed() {
        int r;
        Thread t = Thread.currentThread();
        如果 ( (((((r = UNSAFE.getInt(t, SECONDARY)) ) != 0) {
            r ^= r << 13; /// xorshift
            r ^= r >>> 17;
            r ^= r << 5;
        }
        否则如果((((r = java.util.concurrent.ThreadLocalRandom.current().nextInt()) ==0)
            r = 1; ///避免零
        UNSAFE.putInt(t, SECONDARY, r);
        返回 r;
    }

    ///通过本体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); }
    }

}

 

标签:JUC,code,Thread,调用,park,吊打,源码,线程,unpark
来源: https://www.cnblogs.com/FlyAway2013/p/12655606.html