zoukankan      html  css  js  c++  java
  • 【Java并发编程】:线程挂起、恢复与终止

    挂起和恢复线程

        Thread 的API中包含两个被淘汰的方法,它们用于临时挂起和重启某个线程,这些方法已经被淘汰,因为它们是不安全的,不稳定的。如果在不合适的时候挂起线程(比如,锁定共享资源时),此时便可能会发生死锁条件——其他线程在等待该线程释放锁,但该线程却被挂起了,便会发生死锁。另外,在长时间计算期间挂起线程也可能导致问题。

        下面的代码演示了通过休眠来延缓运行,模拟长时间运行的情况,使线程更可能在不适当的时候被挂起:

    1. public class DeprecatedSuspendResume extends Object implements Runnable{  
    2.   
    3.     //volatile关键字,表示该变量可能在被一个线程使用的同时,被另一个线程修改  
    4.     private volatile int firstVal;  
    5.     private volatile int secondVal;  
    6.   
    7.     //判断二者是否相等  
    8.     public boolean areValuesEqual(){  
    9.         return ( firstVal == secondVal);  
    10.     }  
    11.   
    12.     public void run() {  
    13.         try{  
    14.             firstVal = 0;  
    15.             secondVal = 0;  
    16.             workMethod();  
    17.         }catch(InterruptedException x){  
    18.             System.out.println("interrupted while in workMethod()");  
    19.         }  
    20.     }  
    21.   
    22.     private void workMethod() throws InterruptedException {  
    23.         int val = 1;  
    24.         while (true){  
    25.             stepOne(val);  
    26.             stepTwo(val);  
    27.             val++;  
    28.             Thread.sleep(200);  //再次循环钱休眠200毫秒  
    29.         }  
    30.     }  
    31.       
    32.     //赋值后,休眠300毫秒,从而使线程有机会在stepOne操作和stepTwo操作之间被挂起  
    33.     private void stepOne(int newVal) throws InterruptedException{  
    34.         firstVal = newVal;  
    35.         Thread.sleep(300);  //模拟长时间运行的情况  
    36.     }  
    37.   
    38.     private void stepTwo(int newVal){  
    39.         secondVal = newVal;  
    40.     }  
    41.   
    42.     public static void main(String[] args){  
    43.         DeprecatedSuspendResume dsr = new DeprecatedSuspendResume();  
    44.         Thread t = new Thread(dsr);  
    45.         t.start();  
    46.   
    47.         //休眠1秒,让其他线程有机会获得执行  
    48.         try {  
    49.             Thread.sleep(1000);}   
    50.         catch(InterruptedException x){}  
    51.         for (int i = 0; i < 10; i++){  
    52.             //挂起线程  
    53.             t.suspend();  
    54.             System.out.println("dsr.areValuesEqual()=" + dsr.areValuesEqual());  
    55.             //恢复线程  
    56.             t.resume();  
    57.             try{   
    58.                 //线程随机休眠0~2秒  
    59.                 Thread.sleep((long)(Math.random()*2000.0));  
    60.             }catch(InterruptedException x){  
    61.                 //略  
    62.             }  
    63.         }  
    64.         System.exit(0); //中断应用程序  
    65.     }  
    66. }  

        某次运行结果如下:

      

        从areValuesEqual()返回的值有时为true,有时为false。以上代码中,在设置firstVal之后,但在设置secondVal之前,挂起新线程会产生麻烦,此时输出的结果会为false(情况1),这段时间不适宜挂起线程,但因为线程不能控制何时调用它的suspend方法,所以这种情况是不可避免的。

        当然,即使线程不被挂起(注释掉挂起和恢复线程的两行代码),如果在main线程中执行asr.areValuesEqual()进行比较时,恰逢stepOne操作执行完,而stepTwo操作还没执行,那么得到的结果同样可能是false(情况2)。


         下面我们给出不用上述两个方法来实现线程挂起和恢复的策略——设置标志位。通过该方法实现线程的挂起和恢复有一个很好的地方,就是可以在线程的指定位置实现线程的挂起和恢复,而不用担心其不确定性。  

         对于上述代码的改进代码如下:

    1. public class AlternateSuspendResume extends Object implements Runnable {  
    2.   
    3.     private volatile int firstVal;  
    4.     private volatile int secondVal;  
    5.     //增加标志位,用来实现线程的挂起和恢复  
    6.     private volatile boolean suspended;  
    7.   
    8.     public boolean areValuesEqual() {  
    9.         return ( firstVal == secondVal );  
    10.     }  
    11.   
    12.     public void run() {  
    13.         try {  
    14.             suspended = false;  
    15.             firstVal = 0;  
    16.             secondVal = 0;  
    17.             workMethod();  
    18.         } catch ( InterruptedException x ) {  
    19.             System.out.println("interrupted while in workMethod()");  
    20.         }  
    21.     }  
    22.   
    23.     private void workMethod() throws InterruptedException {  
    24.         int val = 1;  
    25.   
    26.         while ( true ) {  
    27.             //仅当贤臣挂起时,才运行这行代码  
    28.             waitWhileSuspended();   
    29.   
    30.             stepOne(val);  
    31.             stepTwo(val);  
    32.             val++;  
    33.   
    34.             //仅当线程挂起时,才运行这行代码  
    35.             waitWhileSuspended();   
    36.   
    37.             Thread.sleep(200);    
    38.         }  
    39.     }  
    40.   
    41.     private void stepOne(int newVal)   
    42.                     throws InterruptedException {  
    43.   
    44.         firstVal = newVal;  
    45.         Thread.sleep(300);    
    46.     }  
    47.   
    48.     private void stepTwo(int newVal) {  
    49.         secondVal = newVal;  
    50.     }  
    51.   
    52.     public void suspendRequest() {  
    53.         suspended = true;  
    54.     }  
    55.   
    56.     public void resumeRequest() {  
    57.         suspended = false;  
    58.     }  
    59.   
    60.     private void waitWhileSuspended()   
    61.                 throws InterruptedException {  
    62.   
    63.         //这是一个“繁忙等待”技术的示例。  
    64.         //它是非等待条件改变的最佳途径,因为它会不断请求处理器周期地执行检查,   
    65.         //更佳的技术是:使用Java的内置“通知-等待”机制  
    66.         while ( suspended ) {  
    67.             Thread.sleep(200);  
    68.         }  
    69.     }  
    70.   
    71.     public static void main(String[] args) {  
    72.         AlternateSuspendResume asr =   
    73.                 new AlternateSuspendResume();  
    74.   
    75.         Thread t = new Thread(asr);  
    76.         t.start();  
    77.   
    78.         //休眠1秒,让其他线程有机会获得执行  
    79.         try { Thread.sleep(1000); }   
    80.         catch ( InterruptedException x ) { }  
    81.   
    82.         for ( int i = 0; i < 10; i++ ) {  
    83.             asr.suspendRequest();  
    84.   
    85.             //让线程有机会注意到挂起请求  
    86.             //注意:这里休眠时间一定要大于  
    87.             //stepOne操作对firstVal赋值后的休眠时间,即300ms,  
    88.             //目的是为了防止在执行asr.areValuesEqual()进行比较时,  
    89.             //恰逢stepOne操作执行完,而stepTwo操作还没执行  
    90.             try { Thread.sleep(350); }   
    91.             catch ( InterruptedException x ) { }  
    92.   
    93.             System.out.println("dsr.areValuesEqual()=" +   
    94.                     asr.areValuesEqual());  
    95.   
    96.             asr.resumeRequest();  
    97.   
    98.             try {   
    99.                 //线程随机休眠0~2秒  
    100.                 Thread.sleep(  
    101.                         ( long ) (Math.random() * 2000.0) );  
    102.             } catch ( InterruptedException x ) {  
    103.                 //略  
    104.             }  
    105.         }  
    106.   
    107.         System.exit(0); //退出应用程序  
    108.     }  
    109. }  

        运行结果如下:


       线程挂起的位置不确定main线程中执行asr.areValuesEqual()进行比较时,恰逢stepOne操作执行完,而stepTwo操作还没执行)asr.areValuesEqual()操作前,让main线程休眠450ms(>300ms),如果挂起请求发出时,新线程正执行到或即将执行到stepOne操作(如果在其前面的话,就会响应挂起请求,从而挂起线程),那么在stepTwo操作执行前,main线程的休眠还没结束,从而main线程休眠结束后执行asr.areValuesEqual()操作进行比较时,stepTwo操作已经执行完,因此也不会出现输出结果为false的情况。

        可以将ars.suspendRequest()代码后的sleep代码去掉,或将休眠时间改为200(明显小于300即可)后,查看执行结果,会发现结果中依然会有出现false的情况。如下图所示:


       总结:线程的挂起和恢复实现的正确方法是:通过设置标志位,让线程在安全的位置挂起

    终止线程

     终止线程的替代方法:同样是使用标志位,通过控制标志位来终止线程。

  • 相关阅读:
    streamsets 集成 cratedb 测试
    streamsets k8s 部署试用
    streamsets rest api 转换 graphql
    StreamSets sdc rpc 测试
    StreamSets 相关文章
    StreamSets 多线程 Pipelines
    StreamSets SDC RPC Pipelines说明
    StreamSets 管理 SDC Edge上的pipeline
    StreamSets 部署 Pipelines 到 SDC Edge
    StreamSets 设计Edge pipeline
  • 原文地址:https://www.cnblogs.com/web424/p/6807319.html
Copyright © 2011-2022 走看看