您当前的位置: 首页 > 

顧棟

暂无认证

  • 3浏览

    0关注

    227博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【JUC系列】LOCK框架系列之二 核心基础类 LockSupport

顧棟 发布时间:2022-04-25 06:00:00 ,浏览量:3

LockSupport

源码采用JDK8

文章目录
    • LockSupport
    • 主要方法
    • 源码部分
      • park(Object blocker)
      • parkNanos(Object blocker, long nanos)
      • parkUntil(Object blocker, long deadline)
      • unpark(Thread thread)
    • 使用示例
      • Object的wait和notify
      • LockSupport的park和unpark
      • 中断示例
    • 对比
      • Thread.sleep()和Object.wait()的区别
      • Object.wait()和Condition.await()的区别
      • Thread.sleep()和LockSupport.park()的区别
      • Object.wait()和LockSupport.park()的区别

  • 为什么LockSupport也是核心基础类? AQS框架借助于两个类:Unsafe(提供CAS操作)和LockSupport(提供park/unpark操作)
  • 写出分别通过wait/notify和LockSupport的park/unpark实现同步?
  • LockSupport.park()会释放锁资源吗? 那么Condition.await()呢?
  • Thread.sleep()、Object.wait()、Condition.await()、LockSupport.park()的区别? 重点
  • 如果在wait()之前执行了notify()会怎样?
  • 如果在park()之前执行了unpark()会怎样?
主要方法

LockSupport定义了一组公共静态方法,这些方法提供了最基础的线程阻塞和唤醒功能。park开头的代表阻塞,unpark开头的代表唤醒。

方法名说明void park(Object blocker)blocker标识当前线程正在等待的对象(阻塞对象)。阻塞当前线程,在调用unpark和当前线程被中断才能返回void parkNanos(Object blocker, long nanos)blocker标识当前线程正在等待的对象(阻塞对象)。阻塞当前线程,最长不超过nanos纳秒。被阻塞的线程会在超时后自动唤醒返回void parkUntil(Object blocker, long deadline)blocker标识当前线程正在等待的对象(阻塞对象)。阻塞当前线程,直到deadline时间(从1970年到deadline时间的毫秒数)void park()阻塞当前线程,在调用unpark和当前线程被中断才能返回void parkNanos(long nanos)阻塞当前线程,最长不超过nanos纳秒。被阻塞的线程会在超时后自动唤醒返回void parkUntil(long deadline)阻塞当前线程,直到deadline时间void unpark(Thread thread)唤醒处于阻塞状态的线程

注意:拥有参数blocker的方法,在被阻塞的时候,在dump文件中会展示当前线程的正在等待的锁(阻塞对象)。

源码部分

LockSupport的构造函数与成员

public class LockSupport {
    // 私有的构造函数,代表类不可以实例化
    private LockSupport() {} // Cannot be instantiated.

...
...
...

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

}
park(Object blocker)
    public static void park(Object blocker) {
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 第一次设置阻塞对象,当前线程等待这个对象
        setBlocker(t, blocker);
        // 执行后,线程就进入了阻塞状态,直到出现以下情况才被唤醒
        // 1.其他线程执行了unpark(当前线程)来唤醒
        // 2.其他线程中断了当前线程
        // 3.虚假调用(无理由)返回。线程不存在了???
        UNSAFE.park(false, 0L);
        // 第二次设置阻塞对象,此时线程以及被唤醒,所以需要清空阻塞对象
        setBlocker(t, null);
    }
parkNanos(Object blocker, long nanos)
    public static void parkNanos(Object blocker, long nanos) {
        // 等待时长需要大于0
        if (nanos > 0) {
            // 获取当前线程
            Thread t = Thread.currentThread();
            // 第一次设置阻塞对象,当前线程等待这个对象
            setBlocker(t, blocker);
            // 执行后,线程就进入了阻塞状态,直到出现以下情况才被唤醒
            // 1.其他线程执行了unpark(当前线程)来唤醒
            // 2.其他线程中断了当前线程
            // 3.等待的时间超时了
            // 4.虚假调用(无理由)返回。线程不存在了???
            UNSAFE.park(false, nanos);
            // 第二次设置阻塞对象,此时线程以及被唤醒,所以需要清空阻塞对象
            setBlocker(t, null);
        }
    }
parkUntil(Object blocker, long deadline)
    public static void parkUntil(Object blocker, long deadline) {
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 第一次设置阻塞对象,当前线程等待这个对象
        setBlocker(t, blocker);
        // 执行后,线程就进入了阻塞状态,直到出现以下情况才被唤醒
        // 1.其他线程执行了unpark(当前线程)来唤醒
        // 2.其他线程中断了当前线程
        // 3.时间到达deadline
        // 4.虚假调用(无理由)返回。线程不存在了???
        UNSAFE.park(true, deadline);
        // 第二次设置阻塞对象,此时线程以及被唤醒,所以需要清空阻塞对象
        setBlocker(t, null);
    }
unpark(Thread thread)
    public static void unpark(Thread thread) {
        // 线程为不空
        if (thread != null)
            // 释放该线程许可
            UNSAFE.unpark(thread);
    }
使用示例 Object的wait和notify
public class WaitAndNotifyDemo {

    static class MyThread extends Thread{
        @Override
        public void run() {
            // 锁MyThread的实例对象myThread
            synchronized (this) {
                System.out.println("获取锁的当前线程[" + Thread.currentThread().getName() + "] before notify");
                notify();
                System.out.println("获取锁的当前线程[" + Thread.currentThread().getName() + "] after notify");
            }
        }
    }

 public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        // 锁这个对象myThread
        synchronized (myThread) {
            try {
                myThread.start();
                // 主线程睡眠3s
                Thread.sleep(3000);
                System.out.println("获取锁的当前线程[" + Thread.currentThread().getName() + "] before wait");
                // 阻塞主线程
                myThread.wait();
                System.out.println("获取锁的当前线程[" + Thread.currentThread().getName() + "] after wait");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

结果

获取锁的当前线程[main] before wait
获取锁的当前线程[Thread-0] before notify
获取锁的当前线程[Thread-0] after notify
获取锁的当前线程[main] after wait

核心流程图

#mermaid-svg-Smj8NatbPCLNUkCA {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Smj8NatbPCLNUkCA .error-icon{fill:#552222;}#mermaid-svg-Smj8NatbPCLNUkCA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Smj8NatbPCLNUkCA .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Smj8NatbPCLNUkCA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Smj8NatbPCLNUkCA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Smj8NatbPCLNUkCA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Smj8NatbPCLNUkCA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Smj8NatbPCLNUkCA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Smj8NatbPCLNUkCA .marker.cross{stroke:#333333;}#mermaid-svg-Smj8NatbPCLNUkCA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Smj8NatbPCLNUkCA .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Smj8NatbPCLNUkCA text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Smj8NatbPCLNUkCA .actor-line{stroke:grey;}#mermaid-svg-Smj8NatbPCLNUkCA .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Smj8NatbPCLNUkCA .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Smj8NatbPCLNUkCA #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Smj8NatbPCLNUkCA .sequenceNumber{fill:white;}#mermaid-svg-Smj8NatbPCLNUkCA #sequencenumber{fill:#333;}#mermaid-svg-Smj8NatbPCLNUkCA #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Smj8NatbPCLNUkCA .messageText{fill:#333;stroke:#333;}#mermaid-svg-Smj8NatbPCLNUkCA .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Smj8NatbPCLNUkCA .labelText,#mermaid-svg-Smj8NatbPCLNUkCA .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Smj8NatbPCLNUkCA .loopText,#mermaid-svg-Smj8NatbPCLNUkCA .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Smj8NatbPCLNUkCA .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Smj8NatbPCLNUkCA .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Smj8NatbPCLNUkCA .noteText,#mermaid-svg-Smj8NatbPCLNUkCA .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Smj8NatbPCLNUkCA .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Smj8NatbPCLNUkCA .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Smj8NatbPCLNUkCA .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Smj8NatbPCLNUkCA .actorPopupMenu{position:absolute;}#mermaid-svg-Smj8NatbPCLNUkCA .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Smj8NatbPCLNUkCA .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Smj8NatbPCLNUkCA .actor-man circle,#mermaid-svg-Smj8NatbPCLNUkCA line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Smj8NatbPCLNUkCA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} main myThread对象锁 Thread0 【运行状态】 创建线程 myThread 【新建状态】 获取myThread对象锁 成功获取myThread对象锁 启动myThread 【就绪状态】 【运行状态】 synchronized (this) 获取myThread对象锁 获取myThread对象锁失败 【阻塞状态】 获取锁失败 【阻塞状态】 Thread.sleep(3000) 【阻塞状态】 myThread.wait() 释放myThread对象锁 成功释放myThread对象锁 获取myThread对象锁 成功获取myThread对象锁 【就绪状态】 获取锁成功 【运行状态】 唤醒wait阻塞的线程mian\n notify 【就绪状态】 释放myThread对象锁 成功释放myThread对象锁 【死亡状态】 获取myThread对象锁 成功获取myThread对象锁 【运行状态】 释放myThread对象锁 成功释放myThread对象锁 【死亡状态】 main myThread对象锁 Thread0

使用wait/notify实现同步时,必须先调用wait,后调用notify,如果先调用notify,再调用wait,将起不了作用。

LockSupport的park和unpark
import java.util.concurrent.locks.LockSupport;

public class LockSupportDemo {

    static class MyThread extends Thread {
        // 线程对象
        private Object object;

        public MyThread(Object object) {
            this.object = object;
        }

        @Override
        public void run() {
            System.out.println("当前线程[" + Thread.currentThread().getName() + "] before unpark");
            // 休眠1s等待主线程阻塞
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 查看主线程的阻塞对象
            System.out.println("当前线程[" + Thread.currentThread().getName() + "] Blocker info " + LockSupport.getBlocker((Thread) object));
            // 唤醒阻塞的主线程
            LockSupport.unpark((Thread) object);
            System.out.println("当前线程[" + Thread.currentThread().getName() + "] after unpark");
            // 休眠500ms,保证先执行park中的setBlocker(t, null);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 再次获取主线程的blocker
            System.out.println("当前线程[" + Thread.currentThread().getName() + "] Blocker info " + LockSupport.getBlocker((Thread) object));
        }
    }

    public static void main(String[] args) {
        // 将主线程作为参数传入myThread
        MyThread myThread = new MyThread(Thread.currentThread());
        myThread.start();
        System.out.println("当前线程[" + Thread.currentThread().getName() + "] before park");
        // 阻塞主线程 假定阻塞对象是String的对象ParkAndUnparkDemo
        LockSupport.park("ParkAndUnparkDemo");
        System.out.println("当前线程[" + Thread.currentThread().getName() + "] after park");
    }
}

结果

当前线程[main] before park
当前线程[Thread-0] before unpark
当前线程[Thread-0] Blocker info ParkAndUnparkDemo
当前线程[main] after park
当前线程[Thread-0] after unpark
当前线程[Thread-0] Blocker info null

核心流程图

#mermaid-svg-klYI4dinXAOo4NXl {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-klYI4dinXAOo4NXl .error-icon{fill:#552222;}#mermaid-svg-klYI4dinXAOo4NXl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-klYI4dinXAOo4NXl .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-klYI4dinXAOo4NXl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-klYI4dinXAOo4NXl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-klYI4dinXAOo4NXl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-klYI4dinXAOo4NXl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-klYI4dinXAOo4NXl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-klYI4dinXAOo4NXl .marker.cross{stroke:#333333;}#mermaid-svg-klYI4dinXAOo4NXl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-klYI4dinXAOo4NXl .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-klYI4dinXAOo4NXl text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-klYI4dinXAOo4NXl .actor-line{stroke:grey;}#mermaid-svg-klYI4dinXAOo4NXl .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-klYI4dinXAOo4NXl .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-klYI4dinXAOo4NXl #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-klYI4dinXAOo4NXl .sequenceNumber{fill:white;}#mermaid-svg-klYI4dinXAOo4NXl #sequencenumber{fill:#333;}#mermaid-svg-klYI4dinXAOo4NXl #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-klYI4dinXAOo4NXl .messageText{fill:#333;stroke:#333;}#mermaid-svg-klYI4dinXAOo4NXl .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-klYI4dinXAOo4NXl .labelText,#mermaid-svg-klYI4dinXAOo4NXl .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-klYI4dinXAOo4NXl .loopText,#mermaid-svg-klYI4dinXAOo4NXl .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-klYI4dinXAOo4NXl .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-klYI4dinXAOo4NXl .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-klYI4dinXAOo4NXl .noteText,#mermaid-svg-klYI4dinXAOo4NXl .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-klYI4dinXAOo4NXl .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-klYI4dinXAOo4NXl .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-klYI4dinXAOo4NXl .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-klYI4dinXAOo4NXl .actorPopupMenu{position:absolute;}#mermaid-svg-klYI4dinXAOo4NXl .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-klYI4dinXAOo4NXl .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-klYI4dinXAOo4NXl .actor-man circle,#mermaid-svg-klYI4dinXAOo4NXl line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-klYI4dinXAOo4NXl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} main Thread0 【运行状态】 创建线程 myThread 【新建状态】 启动myThread myThread.start() 【就绪状态】 【运行状态】 【阻塞状态】Thread.sleep(1000) 【阻塞状态】 LockSupport.park("ParkAndUnparkDemo") 唤醒主线程 LockSupport.unpark((Thread) object) 【阻塞状态】Thread.sleep(500) 【就绪状态】 【运行状态】 输出“当前线程[main] after park” 【就绪状态】 【运行状态】 输出“当前线程[Thread-0] Blocker info null” 【死亡状态】 【死亡状态】 main Thread0

与wait和notify不同:在先调用unpark,再调用park时,仍能够正确实现同步。不知道为啥???但是代码可以跑

中断示例

在主线程调用park阻塞后,在myThread线程中发出了中断信号,此时主线程会继续运行,也就是说明此时interrupt起到的作用与unpark一样。

import java.util.concurrent.locks.LockSupport;

public class LockSupportDemo {

    static class MyThread extends Thread {
        // 线程对象
        private Object object;

        public MyThread(Object object) {
            this.object = object;
        }

        @Override
        public void run() {
            System.out.println("当前线程[" + Thread.currentThread().getName() + "] before interrupt");
            try {
                // 休眠3s
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Thread thread = (Thread) object;
            // 中断线程
            thread.interrupt();
            System.out.println("当前线程[" + Thread.currentThread().getName() + "] after interrupt");
        }
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread(Thread.currentThread());
        myThread.start();
        System.out.println("当前线程[" + Thread.currentThread().getName() + "] before park");
        // 获取许可
        LockSupport.park("ParkAndUnparkDemo");
        System.out.println("当前线程[" + Thread.currentThread().getName() + "] after park");
    }
}

结果

当前线程[main] before park
当前线程[Thread-0] before interrupt
当前线程[Thread-0] after interrupt
当前线程[main] after park
对比 Thread.sleep()和Object.wait()的区别

锁:sleep不会释放锁资源,wait会释放锁资源

唤醒:sleep自己在特定时间后会自动唤醒,wait可以自己唤醒也支持另一个线程使用Object.notify()唤醒

Object.wait()和Condition.await()的区别

Object.wait()和Condition.await()的原理是基本一致的,不同的是Condition.await()底层是调用LockSupport.park()来实现阻塞当前线程的。

Thread.sleep()和LockSupport.park()的区别

LockSupport.park()还有几个兄弟方法——parkNanos()、parkUtil()等,我们这里说的park()方法统称这一类方法。

  • 从功能上来说,Thread.sleep()和LockSupport.park()方法类似,都是阻塞当前线程的执行,且都不会释放当前线程占有的锁资源;
  • Thread.sleep()没法从外部唤醒,只能自己醒过来;
  • LockSupport.park()方法可以被另一个线程调用LockSupport.unpark()方法唤醒;
  • Thread.sleep()方法声明上抛出了InterruptedException中断异常,所以调用者需要捕获这个异常或者再抛出;
  • LockSupport.park()方法不需要捕获中断异常;
  • Thread.sleep()本身就是一个native方法;
  • LockSupport.park()底层是调用的Unsafe的native方法;
Object.wait()和LockSupport.park()的区别

二者都会阻塞当前线程的运行,他们有什么区别呢? 经过上面的分析相信你一定很清楚了,真的吗? 往下看!

  • Object.wait()方法需要在synchronized块中执行;
  • LockSupport.park()可以在任意地方执行;
  • Object.wait()方法声明抛出了中断异常,调用者需要捕获或者再抛出;
  • LockSupport.park()不需要捕获中断异常;
  • Object.wait()不带超时的,需要另一个线程执行notify()来唤醒,但不一定继续执行后续内容;
  • LockSupport.park()不带超时的,需要另一个线程执行unpark()来唤醒,一定会继续执行后续内容;

参考内容 https://www.pdai.tech/md/java/thread/java-thread-x-lock-LockSupport.html

关注
打赏
1663402667
查看更多评论
立即登录/注册

微信扫码登录

0.0515s