zoukankan      html  css  js  c++  java
  • LockSupport源码分析

    LockSupport源码分析

    LockSupport是Java6引入的一个工具类, 用于挂起和唤醒线程;

    LockSupport 通过提供park() 和 unpark() 方法实现阻塞线程和解除线程阻塞, 实现阻塞与解除阻塞是基于许可(permit), permit相当于一个信号量,只能取0和1, 默认为0;

    • park(): 若permit为1, 则permit减1为0; 若permit为0, 则阻塞当前线程;
    • unpark(Thread): 若permit为0, permit加1; 若permit为1, permit不变; 但效果都是唤醒指定线程;

    由于permit默认为0, 因此一般是先调用park()之后, 当前线程进入阻塞, 然后等待其他线程调用unpark(Thread)唤醒, 或者遇到中断会被唤醒;

    // UNSAFE.park第一个参数var1表示是否为相对时间, 第二个参数var2为超时等待时间
    // var1为fasle时, var2的单位为纳秒, var2为0时, 会一直等待, 直到被unpark()唤醒或者被中断后才会向下执行;
    // var2为true时, 表示绝对时间, var2单位为毫秒 
    public static void park() {
        UNSAFE.park(false, 0L); 
    }
    // 唤醒thread线程, 对一个thread调用一次和多次效果一样;
    public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }
    

    LockSupport的实现

    LockSupport 是基于 sun.misc.Unsafe 实现的;

    1. 内部重要的属性:

    • UNSAFE: Unsafe对象, 提供CAS操作, 获取对象内存中字段的偏移量, 设置对象偏移量对应的字段的值;
    • parkBlockerOffset 存储Thread类对象中parkBlocker字段的偏移量;

    Thread类中, parkBlocker用于存储引起线程阻塞的对象(即: 线程所等待的资源); parkBlocker字段是特地为park()设计的, 在park()调用后, 通过调用setBlocker(Thread t, Object arg)设置线程所等待的资源, 此时用jstack工具进行堆栈分析时, 可以看到该阻塞的线程正在等待的资源arg;

    private static final sun.misc.Unsafe UNSAFE; 
    private static final long parkBlockerOffset;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> tk = Thread.class;
            parkBlockerOffset = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("parkBlocker"));
            ...
        } catch (Exception ex) { throw new Error(ex); }
    }
    

    2. getBlocker(Thread) 与 setBlocker(Thread t, Object arg)源码

    getBlocker 与 setBlocker 使用Unsafe类通过内存偏移量设置的原因:

    因为要设置的parkBlocker的线程已经被阻塞了, 通过调用Thread对象的set方法由于阻塞无法被执行; 然而使用Unsafe类通过内存偏移地址设置并不受Thread对象被阻塞的影响;

    /**
     * 唤醒线程 t
     * @param  t 想要唤醒的线程
     * @return  返回parkBlocker对象
     */
    public static Object getBlocker(Thread t) {
        if (t == null)
            throw new NullPointerException();
        return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
    }
    /**
     * 设置 parkBlocker的值
     * @param t   被阻塞的线程
     * @param arg 引起线程阻塞的资源
     */
    private static void setBlocker(Thread t, Object arg) {
        // Even though volatile, hotspot doesn't need a write barrier here.
        UNSAFE.putObject(t, parkBlockerOffset, arg);
    }
    

    3. park的其他几个方法

    • park(Object blocker)
    • parkNanos(long nanos)
    • parkNanos(Object blocker, long nanos)
    • parkUntil(long deadline)
    • parkUntil(Object blocker, long deadline)

    内部实现源码:

    public static void park(Object blocker) {
        Thread t = Thread.currentThread(); // 获取当前线程 
        setBlocker(t, blocker); // 设置引起当前线程阻塞的资源
        UNSAFE.park(false, 0L); // 将当前线程阻塞
        setBlocker(t, null); // 该线程被唤醒后, 将blocker置为null
    }
    // 阻塞当前线程, 最长不超过nanos纳秒
    public static void parkNanos(long nanos) {
        if (nanos > 0)
            UNSAFE.park(false, nanos); // 超时自动唤醒
    }
    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时间(从1970年开始到deadline时间的毫秒数)
    public static void parkUntil(long deadline) {
        UNSAFE.park(true, deadline);
    }
    public static void parkUntil(Object blocker, long deadline) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(true, deadline);
        setBlocker(t, null);
    }
    

    4. park()/unpark() 与 wait()/notify()区别:

    • park(), wait() 都是阻塞当前线程, 但是wait()需要获取synchronize监视器才能调用;
    • park() 可以设置getBlocker, 使用jstack堆栈分析时更加方便;
    • unpark() 可以唤醒指定线程, 而notify()唤醒一个随机的在该监视器上等待的线程, notifyAll()唤醒所有在该监视器上等待的线程;
  • 相关阅读:
    【WEB前端开发最佳实践系列】高可读的HTML
    【Web前端开发最佳实践系列】标准的HTML代码
    Web服务器配置Gzip压缩提升网站性能
    【Web前端开发最佳实践系列】前端代码推荐和建议
    【前端系列】移动前端开发之viewport的深入理解
    【Spring Boot && Spring Cloud系列】那些Spring Boot中踩过的坑
    【Spring Boot && Spring Cloud系列】Spring Boot的启动器Starter
    【Spring Boot&&Spring Cloud系列】提高数据库访问性能
    【Spring Boot&& Spring Cloud系列】单点登录SSO之OAuth2官方开发文档翻译
    【Spring Boot&& Spring Cloud系列】单点登录SSO概述
  • 原文地址:https://www.cnblogs.com/jxkun/p/9375036.html
Copyright © 2011-2022 走看看