LockSupport是个啥?

一、概览

  • LockSupport 是Java线程阻塞原语,它是创建锁lock和其他同步类的基础。
  • LockSupport为每个线程关联了一个许可证(信号量)许可证的值为取值为0、1,初始值默认为0;
  • LockSupport.park方法调用后,线程是否阻塞取决于当前线程信号量的值,若信号量为1,会立即执行;否则阻塞(若此时中断interrupt发生,则线程继续执行);
  • LockSupport.unpark方法调用后,会将信号量的值修改为1,即放行线程。
  • LockSupport 可以解决因自旋花费大量时间的忙等待。
    在这里插入图片描述

二、代码实例

2.1 LockSupport.unpark唤醒LockSupport.park

2.1.1 代码

public class LockBase {
    public static void main(String[] args) {
        final ParkThread parkThread = new ParkThread();
        parkThread.start();
        System.out.println(System.currentTimeMillis() + " :LockSupport.unpark");
        LockSupport.unpark(parkThread);
    }

    private static void sleep(int secs) {
        try {
            TimeUnit.SECONDS.sleep(secs);
        } catch (InterruptedException e) {
            System.out.println("interrupt");
        }
    }

    static class ParkThread extends Thread{
        @Override
        public void run() {
            System.out.println(System.currentTimeMillis() + " :ParkThread enter");
            LockBase.sleep(5); // 等待5s确保主线程执行完 LockSupport.unpark(parkThread);
            System.out.println(System.currentTimeMillis() + " :LockSupport.park()");
            LockSupport.park(); // 不会阻塞,因为上述语句[LockSupport.unpark(parkThread)]执行后,信号量为1。
            System.out.println(System.currentTimeMillis() + ": ParkThread run");
        }
    }
}

2.1.2 运行结果

在这里插入图片描述
分析:

  • 子线程执行 LockSupport.park 并未阻塞,因为主线程在之前已经执行了LockSupport.unpark,使得子线程的许可证可用,即值为1;若不执行LockSupport.unpark,子线程将一直阻塞;
  • LockSupport.unparkLockSupport.park 二者没有执行顺序限制,本质都是在操作信号量,前者写,后者读。
  • LockSupport.unpark(Thread thread) 可以指定线程。

2.2 中断interrupt唤醒LockSupport.park

2.2.1代码

public class LockBase {
    public static void main(String[] args) {
        final ParkThread parkThread = new ParkThread();
        parkThread.start();
        sleep(5);
        System.out.println(System.currentTimeMillis() + " :parkThread.interrupt");
        parkThread.interrupt(); // 中断线程
    }

    private static void sleep(int secs) {
        try {
            TimeUnit.SECONDS.sleep(secs);
        } catch (InterruptedException e) {
            System.out.println("interrupt");
        }
    }

    static class ParkThread extends Thread{
        @Override
        public void run() {
            System.out.println(System.currentTimeMillis() + " :ParkThread enter");
            System.out.println(System.currentTimeMillis() + " :LockSupport.park()");
            LockSupport.park(); // 阻塞,因为信号量为0。
            System.out.println(System.currentTimeMillis() + ": ParkThread run");
        }
    }
}

2.2.2 运行结果

在这里插入图片描述
分析:

  • 子线程执行 LockSupport.park 后阻塞了,因为信号量初始值默认为0
  • 子线程被中断interrupt后,线程不再阻塞了,开始往下执行了。即LockSupport.park会响应中断interrupt,从而不再阻塞线程。

三、源码

  • park
	public static void park() {
		U.park(false, 0L);
	}
    public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker); // 设置阻塞原因。
        U.park(false, 0L); // 调用底层阻塞线程
        setBlocker(t, null); // 清理阻塞原因,防止调用getBlocker()获取到错误原因。注意此行代码并非立刻执行,会直到阻塞解除才执行。
    }
	// 带时间的阻塞。
    public static void parkNanos(long nanos) {
        if (nanos > 0)
            U.park(false, nanos);
    }
    public static void parkUntil(long deadline) {
        U.park(true, deadline);
    }
    public static void parkUntil(Object blocker, long deadline) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        U.park(true, deadline);
        setBlocker(t, null);
    }

四、补充

4.1 LockSupport.park执行后,线程状态 会变成WAITING,而synchronzed 会使线程阻塞,线程会进入 BLOCKED 状态。

4.1.1 代码

public class LockBase {
    public static void main(String[] args) {
        final ParkThread parkThread = new ParkThread();
        parkThread.start();
        sleep(5);
        System.out.println(parkThread.getState()); // 线程状态为WAITING
    }

    private static void sleep(int secs) {
        try {
            TimeUnit.SECONDS.sleep(secs);
        } catch (InterruptedException e) {
            System.out.println("interrupt");
        }
    }

    static class ParkThread extends Thread{
        @Override
        public void run() {
            System.out.println(System.currentTimeMillis() + " :ParkThread enter");
            System.out.println(System.currentTimeMillis() + " :LockSupport.park()");
            LockSupport.park(); // 阻塞
            System.out.println(System.currentTimeMillis() + ": ParkThread run");
        }
    }
}

4.1.2 执行结果

在这里插入图片描述
分析

  • LockSupport.park执行后,在等待信号量可用,并非synchronzed 底层的Mutex Lock

4.2 推荐使用 LockSupport 来做线程的阻塞与唤醒(线程间协同工作),而非synchronzed 中的wait notify,因为它具备如下优点:

  • 以线程为操作对象更符合阻塞线程的直观语义
  • 操作更精准,可以准确地唤醒某一个线程(notify 随机唤醒一个线程,notifyAll 唤醒所有等待的线程)
  • 无需竞争锁对象(以线程作为操作对象),不会因竞争锁对象产生死锁问题
  • LockSupport.unparkLockSupport.park 没有严格的执行顺序,不会因执行顺序引起永久阻塞。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值