【Java并发编程】:线程挂起、恢复与终止
挂起和恢复线程
Thread 的API中包含两个被淘汰的方法,它们用于临时挂起和重启某个线程,这些方法已经被淘汰,因为它们是不安全的,不稳定的。如果在不合适的时候挂起线程(比如,锁定共享资源时),此时便可能会发生死锁条件——其他线程在等待该线程释放锁,但该线程却被挂起了,便会发生死锁。另外,在长时间计算期间挂起线程也可能导致问题。
下面的代码演示了通过休眠来延缓运行,模拟长时间运行的情况,使线程更可能在不适当的时候被挂起:
- public class DeprecatedSuspendResume extends Object implements Runnable{
- //volatile关键字,表示该变量可能在被一个线程使用的同时,被另一个线程修改
- private volatile int firstVal;
- private volatile int secondVal;
- //判断二者是否相等
- public boolean areValuesEqual(){
- return ( firstVal == secondVal);
- }
- public void run() {
- try{
- firstVal = 0;
- secondVal = 0;
- workMethod();
- }catch(InterruptedException x){
- System.out.println("interrupted while in workMethod()");
- }
- }
- private void workMethod() throws InterruptedException {
- int val = 1;
- while (true){
- stepOne(val);
- stepTwo(val);
- val++;
- Thread.sleep(200); //再次循环钱休眠200毫秒
- }
- }
- //赋值后,休眠300毫秒,从而使线程有机会在stepOne操作和stepTwo操作之间被挂起
- private void stepOne(int newVal) throws InterruptedException{
- firstVal = newVal;
- Thread.sleep(300); //模拟长时间运行的情况
- }
- private void stepTwo(int newVal){
- secondVal = newVal;
- }
- public static void main(String[] args){
- DeprecatedSuspendResume dsr = new DeprecatedSuspendResume();
- Thread t = new Thread(dsr);
- t.start();
- //休眠1秒,让其他线程有机会获得执行
- try {
- Thread.sleep(1000);}
- catch(InterruptedException x){}
- for (int i = 0; i < 10; i++){
- //挂起线程
- t.suspend();
- System.out.println("dsr.areValuesEqual()=" + dsr.areValuesEqual());
- //恢复线程
- t.resume();
- try{
- //线程随机休眠0~2秒
- Thread.sleep((long)(Math.random()*2000.0));
- }catch(InterruptedException x){
- //略
- }
- }
- System.exit(0); //中断应用程序
- }
- }
某次运行结果如下:
从areValuesEqual()返回的值有时为true,有时为false。以上代码中,在设置firstVal之后,但在设置secondVal之前,挂起新线程会产生麻烦,此时输出的结果会为false(情况1),这段时间不适宜挂起线程,但因为线程不能控制何时调用它的suspend方法,所以这种情况是不可避免的。
当然,即使线程不被挂起(注释掉挂起和恢复线程的两行代码),如果在main线程中执行asr.areValuesEqual()进行比较时,恰逢stepOne操作执行完,而stepTwo操作还没执行,那么得到的结果同样可能是false(情况2)。
下面我们给出不用上述两个方法来实现线程挂起和恢复的策略——设置标志位。通过该方法实现线程的挂起和恢复有一个很好的地方,就是可以在线程的指定位置实现线程的挂起和恢复,而不用担心其不确定性。
对于上述代码的改进代码如下:
- public class AlternateSuspendResume extends Object implements Runnable {
- private volatile int firstVal;
- private volatile int secondVal;
- //增加标志位,用来实现线程的挂起和恢复
- private volatile boolean suspended;
- public boolean areValuesEqual() {
- return ( firstVal == secondVal );
- }
- public void run() {
- try {
- suspended = false;
- firstVal = 0;
- secondVal = 0;
- workMethod();
- } catch ( InterruptedException x ) {
- System.out.println("interrupted while in workMethod()");
- }
- }
- private void workMethod() throws InterruptedException {
- int val = 1;
- while ( true ) {
- //仅当贤臣挂起时,才运行这行代码
- waitWhileSuspended();
- stepOne(val);
- stepTwo(val);
- val++;
- //仅当线程挂起时,才运行这行代码
- waitWhileSuspended();
- Thread.sleep(200);
- }
- }
- private void stepOne(int newVal)
- throws InterruptedException {
- firstVal = newVal;
- Thread.sleep(300);
- }
- private void stepTwo(int newVal) {
- secondVal = newVal;
- }
- public void suspendRequest() {
- suspended = true;
- }
- public void resumeRequest() {
- suspended = false;
- }
- private void waitWhileSuspended()
- throws InterruptedException {
- //这是一个“繁忙等待”技术的示例。
- //它是非等待条件改变的最佳途径,因为它会不断请求处理器周期地执行检查,
- //更佳的技术是:使用Java的内置“通知-等待”机制
- while ( suspended ) {
- Thread.sleep(200);
- }
- }
- public static void main(String[] args) {
- AlternateSuspendResume asr =
- new AlternateSuspendResume();
- Thread t = new Thread(asr);
- t.start();
- //休眠1秒,让其他线程有机会获得执行
- try { Thread.sleep(1000); }
- catch ( InterruptedException x ) { }
- for ( int i = 0; i < 10; i++ ) {
- asr.suspendRequest();
- //让线程有机会注意到挂起请求
- //注意:这里休眠时间一定要大于
- //stepOne操作对firstVal赋值后的休眠时间,即300ms,
- //目的是为了防止在执行asr.areValuesEqual()进行比较时,
- //恰逢stepOne操作执行完,而stepTwo操作还没执行
- try { Thread.sleep(350); }
- catch ( InterruptedException x ) { }
- System.out.println("dsr.areValuesEqual()=" +
- asr.areValuesEqual());
- asr.resumeRequest();
- try {
- //线程随机休眠0~2秒
- Thread.sleep(
- ( long ) (Math.random() * 2000.0) );
- } catch ( InterruptedException x ) {
- //略
- }
- }
- System.exit(0); //退出应用程序
- }
- }
运行结果如下:
线程挂起的位置不确定main线程中执行asr.areValuesEqual()进行比较时,恰逢stepOne操作执行完,而stepTwo操作还没执行)asr.areValuesEqual()操作前,让main线程休眠450ms(>300ms),如果挂起请求发出时,新线程正执行到或即将执行到stepOne操作(如果在其前面的话,就会响应挂起请求,从而挂起线程),那么在stepTwo操作执行前,main线程的休眠还没结束,从而main线程休眠结束后执行asr.areValuesEqual()操作进行比较时,stepTwo操作已经执行完,因此也不会出现输出结果为false的情况。
可以将ars.suspendRequest()代码后的sleep代码去掉,或将休眠时间改为200(明显小于300即可)后,查看执行结果,会发现结果中依然会有出现false的情况。如下图所示:
总结:线程的挂起和恢复实现的正确方法是:通过设置标志位,让线程在安全的位置挂起
终止线程
终止线程的替代方法:同样是使用标志位,通过控制标志位来终止线程。
【Java并发编程】:线程挂起、恢复与终止的更多相关文章
- Java 并发编程 | 线程池详解
原文: https://chenmingyu.top/concurrent-threadpool/ 线程池 线程池用来处理异步任务或者并发执行的任务 优点: 重复利用已创建的线程,减少创建和销毁线程造 ...
- java并发编程 线程基础
java并发编程 线程基础 1. java中的多线程 java是天生多线程的,可以通过启动一个main方法,查看main方法启动的同时有多少线程同时启动 public class OnlyMain { ...
- java并发编程 | 线程详解
个人网站:https://chenmingyu.top/concurrent-thread/ 进程与线程 进程:操作系统在运行一个程序的时候就会为其创建一个进程(比如一个java程序),进程是资源分配 ...
- Java并发编程:线程间通信wait、notify
Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
- Java并发编程:线程和进程的创建(转)
Java并发编程:如何创建线程? 在前面一篇文章中已经讲述了在进程和线程的由来,今天就来讲一下在Java中如何创建线程,让线程去执行一个子任务.下面先讲述一下Java中的应用程序和进程相关的概念知识, ...
- Java并发编程——线程池的使用
在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...
- Java并发编程——线程池
本文的目录大纲: 一.Java中的ThreadPoolExecutor类 二.深入剖析线程池实现原理 三.使用示例 四.如何合理配置线程池的大小 一.Java中的ThreadPoolExecutor类 ...
- Java并发编程-线程可见性&线程封闭&指令重排序
一.指令重排序 例子如下: public class Visibility1 { public static boolean ready; public static int number; } pu ...
- Java并发编程--线程封闭(Ad-hoc封闭 栈封闭 ThreadLocal)
线程封闭实现好的并发是一件困难的事情,所以很多时候我们都想躲避并发.避免并发最简单的方法就是线程封闭.什么是线程封闭呢?就是把对象封装到一个线程里,只有这一个线程能看到此对象.那么这个对象就算不是线程 ...
- JAVA 并发编程-线程范围内共享变量(五)
线程范围内共享变量要实现的效果为: 多个对象间共享同一线程内的变量 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsi ...
随机推荐
- HDU 3079 Vowel Counting (水题。。。判断元音)
题意:n个字符串,如果元音就是输出大写,否则输出小写. 析:没啥可说的,只要判断AEIOU就OK了. 代码如下: #include <iostream> #include <cstd ...
- hdu1081 To The Max 2016-09-11 10:06 29人阅读 评论(0) 收藏
To The Max Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total ...
- IDEA13 SVN配置
这个算是解决了,idea13是支持svn 1.8. 步骤: 1.下载svn客户端软件,小乌龟:TortoiseSVN.安装的时候,一定要选择安装svn命令行的那个选项.当前版本1.8默认只会忽略命令行 ...
- Spring Boot 应用系列 1 -- Spring Boot 2 整合Spring Data JPA和Druid,双数据源
最近Team开始尝试使用Spring Boot + Spring Data JPA作为数据层的解决方案,在网上逛了几圈之后发现大家并不待见JPA,理由是(1)MyBatis简单直观够用,(2)以Hib ...
- bash基本命令速查表
来源:https://github.com/skywind3000/awesome-cheatsheets/blob/master/languages/bash.sh ################ ...
- js 日期去掉 -
语法 stringObj.replace(rgExp, replaceText) replace 方法的语法包括下述部分: stringObj 必选项.要执行该替换的 String 对象或文字.该对象 ...
- C# GDI绘制仪表盘(纯代码实现)
纯代码实现GDI绘制仪表盘,效果在代码下面. public partial class HalfDashboardUc : UserControl { /// <summary> /// ...
- RabbitMQ广播模式
广播模式:1对多,produce发送一则消息多个consumer同时收到.注意:广播是实时的,produce只负责发出去,不会管对端是否收到,若发送的时刻没有对端接收,那消息就没了,因此在广播模式下设 ...
- POST与GET请求的区别
https://www.cnblogs.com/logsharing/p/8448446.html 返回主页 在途中# 博客园首页新随笔联系订阅管理 随笔 - 33 文章 - 5 评论 - 40 GE ...
- python学习笔记6-集合
# 集合是无序且不可重复的元素的集合 a = set([1,3,1,3,3,2,2,5]) a # {1, 2, 3, 5} b = set(range(2,5)) b # {2, 3, 4} # 1 ...