Java并发基础(10):LockSupport

目录

1、LockSupport简介

2、方法

3、个人理解

4、验证

1、unpark在thread start之前调用 无效

2、线程初始有没有许可?

2、添加许可并消耗许可

3、许可上限为 1

4、中断可以使 park 继续执行并不会抛出异常

5、源码分析结果

6、park/unpark 和 wait/notify 区别


1、LockSupport简介

        LockSupport位于java.util.concurrent.locks包下。LockSupprot是线程的阻塞原语,用来阻塞线程和唤醒线程。每个使用LockSupport的线程都会与一个许可关联,如果该许可可用,并且可在线程中使用,则调用park()将会立即返回,否则可能阻塞。如果许可尚不可用,则可以调用 unpark 使其可用。但是注意许可不可重入,也就是说只能调用一次park()方法,否则会一直阻塞。

LockSupport 类每个使用它的线程关联一个许可(在意义上的Semaphore类)。 如果许可可用,调用 park 将立即返回,并在此过程中消费它; 否则可能阻塞。如果许可不是可用,可以调用 unpark 使得许可可用。(但与Semaphore不同,许可不能累积。最多有一个。)

方法 park 和 unpark 提供了阻塞的有效手段和解锁线程不会遇到死锁问题,而 Thread.suspend 和 Thread.resume 是不能用于这种目的:因为许可的存在,一个线程调用 park 和另一个线程试图 unpark 它之间的竞争将保持活性。 此外,如果调用者线程被中断,park 将返回,并且支持设置超时。 该 park 方法也可能返回在其他任何时间,“毫无理由”,因此通常必须在一个循环中调用的返回后重新检查条件。 在这个意义上park作为“忙碌等待”不会浪费太多的时间自旋的优化,但必须以配对 unpark 使用。

这三种形式的 park 还支持 blocker 对象参数。而线程被阻塞时是允许使用监测和诊断工具,以确定线程被阻塞的原因。(诊断工具可以使用getBlocker(Thread) 方法 。)同时推荐使用带有 blocker 参数的 park方法,通常做法是 blocker 被设置为 this 。

2、方法

  • unPark() 方法:使得所给的线程的permit置为ok。如果所给的线程还没开始,则这个方法并不保证有效。
  • park() 方法:如果当前线程的permit是ok的,则继续执行,否则阻塞等待,直到以下任意一件事发生为止:
    1. 其他线程调用unpark方法,传入的thread是当前线程。
    2. 其他线程interrupt当前线程

3、个人理解

  1. 许可(permit)的上限是1,也就是说只有 0 或 1 。
  2. park: 没有许可的时候,permit 为 0 ,调用 park 会阻塞;有许可的时候,permit 为 1 , 调用 park 会扣除一个许可,然后返回。
  3. unpark:没有许可的时候,permit 为 0 ,调用 unpark 会增加一个许可,因为许可上限是 1 , 所以调用多次也只会为 1 个。
  4. 线程初始的时候是没有许可的。
  5. park 的当前线程如果被中断,会立即返回,并不会抛出中断异常。
  6. park 方法的调用一般要放在一个循环判断体里面。

4、验证

1、unpark在thread start之前调用 无效

  public static void main(String[] args) throws InterruptedException{
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread1 start");
 
                try{
                    Thread.sleep(2000);
                } catch (Exception e){
                    e.printStackTrace();
                }
 
                System.out.println("thread1 run before park");
 
                LockSupport.park();
 
                System.out.println("thread1 run after park");
            }
        });
        LockSupport.unpark(thread1);
        thread1.start();
 
        System.out.println("main thread");
     }
 }
main thread
thread1 start
thread1 run before park
(程序没有退出,线程1一直挂起)
  • unpark必须在thread start之后才有用,之前调用没有任何效果;
  • thread start之后,unpark在park之前还是之后,作用是一样的,都会重新唤醒线程;

2、线程初始有没有许可?

public class LockSupportTest1 {

    public static void main(String[] args) throws InterruptedException {
        
        System.out.println("开始执行……");
        LockSupport.park();
        System.out.println("LockSupport park 之后……");
    }
}
开始执行……

执行后会发现,代码在 park 处阻塞。说明,线程初始是没有许可的。

2、添加许可并消耗许可

public class LockSupportTest1 {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("开始执行……");
        LockSupport.unpark(Thread.currentThread());
        System.out.println("执行 - park");
        LockSupport.park();
        System.out.println("LockSupport park 之后……");
    }
}
开始执行……
执行 - park
LockSupport park 之后……

通过上面示例可以看出:

  1. 执行 unpark 可以进行给予指定线程一个证书。
  2. 线程当前被 park 阻塞,此时给予证书之后, park 会消耗证书,然后继续执行。

3、许可上限为 1

public class LockSupportTest1 {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("unpark 1次");
        LockSupport.unpark(Thread.currentThread());
        System.out.println("unpark 2次");
        LockSupport.unpark(Thread.currentThread());
        System.out.println("执行 - park 1 次");
        LockSupport.park();
        System.out.println("执行 - park 2 次");
        LockSupport.park();
        System.out.println("LockSupport park 之后……");
    }
}
unpark 1次
unpark 2次
执行 - park 1 次
执行 - park 2 次

说明:线程阻塞,可以看出 permit 只能有一个

4、中断可以使 park 继续执行并不会抛出异常

public class LockSupportTest1 {

    public static void main(String[] args) throws InterruptedException {
           Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程 " + Thread.currentThread().getName() + "开始执行 park");
                LockSupport.park(this);
                System.out.println("线程 " + Thread.currentThread().getName() + "执行 park 结束");
                System.out.println("线程 " + Thread.currentThread().getName() + "开始执行 park 第二次");
                LockSupport.park(this);
                System.out.println("线程 " + Thread.currentThread().getName() + "执行 park 第二次 结束");
            }
        });
        try {
            thread.start();
            // 保证 上面线程先执行,然后再主线程
            Thread.sleep(5000);
            System.out.println("开始执行 unpark(thread)");
            LockSupport.unpark(thread);
            thread.interrupt();
            Thread.sleep(5000);
            System.out.println("执行 unpark(thread) 结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
线程 Thread-0开始执行 park
开始执行 unpark(thread)
线程 Thread-0执行 park 结束
线程 Thread-0开始执行 park 第二次
线程 Thread-0执行 park 第二次 结束
执行 unpark(thread) 结束
  1. 可以看出线程中断,park 会继续执行,并且没有抛出异常。
  2. thread.interrupt(); 调用之后, 设置线程中断标示,unpark 没有清除中断标示,第二个 park 也会继续执行。

5、源码分析结果

  1. 在底层维护了一个 _counter 通过更新 _counter 的值来标示是否有证明。
  2. 在 park 时,判断 _counter 为 0,则阻塞等待,为 1 则获得更新为 0 并返回。
  3. 在 unpark 时,判断 _counter 为 0,则给予凭证,并唤醒线程,为 1 则直接返回。

6、park/unpark 和 wait/notify 区别

  1. park 阻塞当前线程,unpark 唤醒指定线程。
  2. wait() 需要结合锁使用,并释放锁资源,如果没有设置超时时间,则需要 notify() 唤醒。而 notify() 是随机唤醒线程。

原文:https://bbs.huaweicloud.com/blogs/206531

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值