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

Wesley13
• 阅读 420

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

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

01

 睡眠

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

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

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

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

02

 挂起与恢复

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

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

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

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

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

03

 死锁问题

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

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

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

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

- END -

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

本文分享自微信公众号 - 码农架构(iByteCoding)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
java 线程篇 之CyclicBarrier、CountDownLatch、Semaphore
java提供了很多控制线程到达某一状态导致之前阻塞线程运行的函数,这些在控制任务执行提供了很大的便利,比如在zookper使用Semaphore实现分布式锁1、CountDownLatchcountDownLatch提供await(),CountDownLatch()来控制,前面我很多例子,使用这个来模拟多线程运行的,所以这里不过多介绍2
Wesley13 Wesley13
2年前
java多线程常见问题
Java多线程是什么Java提供的并发(同时、独立)处理多个任务的机制。多个线程共存于同一JVM进程里面,所以共用相同的内存空间,较之多进程,多线程之间的通信更轻量级。依我的理解,Java多线程完全就是为了提高CPU的利用率。Java的线程有4种状态,新建(New)、运行(Runnable)、阻塞(Blocked)、结束(Dead),关键就在于阻塞(Bl
Stella981 Stella981
2年前
CountDownLatch和CylicBarrier以及Semaphare你使用过吗
CountDownLatch是什么CountDownLatch的字面意思:倒计时门栓它的功能是:让一些线程阻塞直到另一些线程完成一系列操作后才唤醒。它通过调用await方法让线程进入阻塞状态等待倒计时0时唤醒。它通过线程调用countDown方法让倒计时中的计数器减去1,当计数器为0时,会唤醒哪些因为调用了await而阻塞的线程。
Stella981 Stella981
2年前
Noark入门之线程模型
0x00单线程多进程单线程与单进程多线程的目的都是想尽可能的利用CPU,减少CPU的空闲时间,特别是多核环境,今天咱不做深度解读,跳过...0x01线程池锁最早的一部分游戏服务器是采用线程池的方式来处理玩家的业务请求,以达最大限度的利用多核优势来提高处理业务能力。但线程池同时也带来了并发问题,为了解决同一玩家多个业务请求不被
Wesley13 Wesley13
2年前
Java 并发编程:如何防止在线程阻塞与唤醒时死锁
Java并发编程:多线程如何实现阻塞与唤醒(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU3OTc1MDM1Mg%3D%3D%26mid%3D2247494798%26idx%3D1%26sn%3D4b27253ee84
Wesley13 Wesley13
2年前
Java 并发编程:多线程如何实现阻塞与唤醒
线程的阻塞和唤醒在多线程并发过程中是一个关键点,当线程数量达到很大的数量级时,并发可能带来很多隐蔽的问题。如何正确暂停一个线程,暂停后又如何在一个要求的时间点恢复,这些都需要仔细考虑的细节。Java为我们提供了多种API来对线程进行阻塞和唤醒操作,比如suspend与resume、sleep、wait与notify以及park与unpark等等。!(
Wesley13 Wesley13
2年前
Java线程与多线程
1线程与多线程1.1线程是什么?线程(Thread)是一个对象(Object)。用来干什么?Java线程(也称JVM线程)是Java进程内允许多个同时进行的任务。该进程内并发的任务成为线程(Thread),一个进程里至少一个线程。Java程序采用多线程方式来支持大量的并发请求处理,程序如果在
贾蓁 贾蓁
1个月前
马士兵「Java多线程与高并发」从入门到精髓
马士兵「Java多线程与高并发」从入门到精髓download》http://quangneng.com/4203/java多线程与高并发是一个非常广泛的话题,涵盖了许多相关的概念和技术。从入门到精髓需要掌握以下几个方面的知识和技能:理解多线程基础:Java