java 多线程 阻塞唤醒_Java并发编程:多线程如何实现阻塞与唤醒

线程的阻塞和唤醒在多线程并发过程中是一个关键点,当线程数量达到很大的数量级时,并发可能带来很多隐蔽的问题。如何正确暂停一个线程,暂停后又如何在一个要求的时间点恢复,这些都需要仔细考虑的细节。Java为我们提供了多种API来对线程进行阻塞和唤醒操作,比如suspend与resume、sleep、wait与notify以及park与unpark等等。

ae938e62483bc4610b3b05c25236f7bd.png

01

睡眠

控制线程阻塞与唤醒的最简单方式就是sleep了,Java通过sleep(n)方法能让线程进入到阻塞等待状态,直到休眠时间达到指定值后自动唤醒。该方法简单也常用,但这种方式比较死板,需要我们预先确定线程进入阻塞的时间。而有些场景实际上我们根本没办法确定睡眠时间,这是sleep方式的最大劣势。

298dbb5bd3ea44346d479ae116706e48.png

sleep的使用很简单,下面为一个例子。让当前线程睡眠2000ms,最终输出为"Sleep time in ms = 2000"。

b5b55d8f02152cb275d2e02b5d7b5fac.png

02

挂起与恢复

在Java发展史上曾经使用suspend()、resume()方法对于线程进行阻塞唤醒,它能够在代码中控制阻塞和唤醒的时间节点,比起sleep()方法更加灵活。比如线程启动后在某个时间点需要让它挂起,这可以使用suspend方法,而当要重新唤醒它时则使用resume方法。

13292ce1010b9b22571a01576356d02e.png

下面代码为例看suspend与resume组合的实现,Thread2启动后输出"Second thread is suspended itself",接着自己将自己挂起。在等待2000ms后由主线程resume恢复,然后Thread2继续输出"Second thread runs again"。

8ca1205fa81cf02725471190f119feeb.png

注意:suspend(),resume(),stop()这样的方法都被标注为过期方法,因为其不会保证释放资源,容易产生死锁,所以不建议使用。

03

死锁问题

suspend与resume的组合存在很多问题,比较典型的还是死锁问题。如下代码,主要的逻辑代码是主线程启动线程mt一段时间后尝试使用suspend()让线程挂起,最后通过resume()恢复线程。但现实并不如愿,执行到suspend()时将一直卡住,你永远等不来“can you get here?”的输出。

8183e7f2633e9959d737b6550282fb64.png

为什么会产生上面的现象呢?其实是由死锁导致。乍一看感觉一点问题都没有,线程的任务仅仅只是简单地打印字符串。其实问题的根源隐藏得较深,主线程启动了线程mt后,线程mt开始执行execute()方法,不断打印字符串。

问题就出现在System.out.println,由于println被声明为一个同步方法,执行时将对System类的out(PrintStream类的一个实例)单例属性加同步锁。而suspend()方法挂起线程但并不释放锁,在线程mt被挂起后主线程调用System.out.println同样需要获取System类out对象的同步锁才能打印“can you get here?”。主线程就一直在等待同步锁而mt线程不释放锁,这就导致了死锁的产生。

- END -

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值