当前位置 : 主页 > 编程语言 > java >

Java LockSupport常用方法的源码分析

来源:互联网 收集:自由互联 发布时间:2023-03-17
目录 LockSupport类常用方法源码 wait/notify方法和park/unpark方法区别 LockSupport类常用方法源码 LockSupport只是一个简单的基础类,位于java.util.concurrent.locks包下,多用于线程的阻塞和唤醒,因此
目录
  • LockSupport类常用方法源码
  • wait/notify方法和park/unpark方法区别

LockSupport类常用方法源码

LockSupport只是一个简单的基础类,位于java.util.concurrent.locks包下,多用于线程的阻塞和唤醒,因此LockSupport也被称为其他线程的工具类。

LockSupport类的源码有标注,LockSupport类无法实例化。LockSupport类的底层是有Unsafe类实现的,LockSupport加载时的初始化也用到了Unsafe获取成员的偏移量,其源码如下:

    // 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); }
    }

LockSupport类中有一些核心的线程操作方法,多用于线程的阻塞与唤醒。

调用park()方法使线程阻塞:

    public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, 0L);
        setBlocker(t, null);
    }
    private static void setBlocker(Thread t, Object arg) {
        // Even though volatile, hotspot doesn't need a write barrier here.
        UNSAFE.putObject(t, parkBlockerOffset, arg);
    }

调用park(Object blocker)对传入的线程进行阻塞

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

在截止时间之前阻塞传入的某个线程:

    public static void parkUntil(Object blocker, long deadline) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(true, deadline);
        setBlocker(t, null);
    }

在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);
        }
    }

唤醒传入的线程:

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

wait/notify方法和park/unpark方法区别

LockSupport类中的方法还有很多,在此先列举到这里。当我们看到阻塞和唤醒方法时,我们会联想到另一组唤醒方法wait()和notify(),这两组方法还是有所区别的。

这里直接记录下结论:wait和notify方法只能在同步代码块中使用(即必须与synchronized连用);必须先执行wait方法,然后再执行notify方法唤醒线程,调换顺序的话线程仍处于阻塞状态。

而park()和unpark()方法与之不同,这里可以通过代码运行结果来看:

package XIAOWEI;
import java.util.concurrent.locks.LockSupport;

​​​​​​​public class Xiaowei{
    public static void main(String[] args) {
        Thread A = new Thread(()-> {
            System.out.println("线程A已经被阻塞QWQ");
            LockSupport.park();
            System.out.println("线程A被线程B唤醒啦~~~");
        });
        A.start();
        new Thread(()->{
            System.out.println("线程B在唤醒线程A ing~~~");
            LockSupport.unpark(A);
        },"B").start();
    }
}

那如果我们先通过线程B唤醒线程A,然后再让线程A阻塞呢(让线程A的阻塞休眠两秒)?

package XIAOWEI;
import java.util.concurrent.locks.LockSupport;
public class Xiaowei {
    public static void main(String[] args) {
        Thread A = new Thread(()-> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程A已经被阻塞QWQ(第二版)");
            LockSupport.park();
            System.out.println("线程A被线程B唤醒啦~~~(第二版)");
        });
        A.start();
        new Thread(()->{
            System.out.println("线程B在唤醒线程A ing~~~驾驾驾");
            LockSupport.unpark(A);
        },"B").start();
    }
}

由上面输出结果来看,虽然线程B先唤醒了线程A,然后线程A再开始阻塞,但是线程A还是处于唤醒状态,这是为什么呢?

接下来我找了段LockSupport类中的注释,其实有时看看注释也挺有意思的哈哈:

 * <p>This class associates, with each thread that uses it, a permit
 * (in the sense of the {@link java.util.concurrent.Semaphore
 * Semaphore} class). A call to {@code park} will return immediately
 * if the permit is available, consuming it in the process; otherwise
 * it <em>may</em> block.  A call to {@code unpark} makes the permit
 * available, if it was not already available. (Unlike with Semaphores
 * though, permits do not accumulate. There is at most one.)

这段话大意是说,LockSupport类使用permits这个东西来实现线程的阻塞和唤醒。每一个线程都会使用到(拥有)permit,且permit的值默认为0。接着它又说,这个概念和Semaphore信号量差不多,但是permit的值只有0和1两个值。哦~原来是这样。

对于上面例子,线程B调用unpark方法唤醒A后,会使得线程A的permit值为1,当线程调用park方法使自己阻塞时,发现自己已经有许可(permit)了,就会继续向下执行业务,而不会阻塞不动。

到此这篇关于Java LockSupport常用方法的源码分析的文章就介绍到这了,更多相关Java LockSupport内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!

上一篇:MyBatis高级映射ResultMap解决属性问题
下一篇:没有了
网友评论