java多线程(八)为什么弃用stop和suspend

本文详细解析了Java中stop和suspend方法的不安全性,并提供了安全终止线程和阻塞线程的方法。通过设置volatile变量flag和使用Lock与Condition,实现了线程的优雅终止和安全阻塞。


初始的java版本中定义了一个stop方法来终止一个线程还定义了一个suspend方法来阻塞一个线程,直到另一个线程调用resume方法。这两个方法在Java SE 1.2之后就被弃用了,因为这两种方法都不安全,下面我们分别来讨论一下为什么不安全和应该怎样做才是安全的。


一、stop方法为什么不安全


其实stop方法天生就不安全,因为它在终止一个线程时会强制中断线程的执行,不管run方法是否执行完了,并且还会释放这个线程所持有的所有的锁对象。这一现象会被其它因为请求锁而阻塞的线程看到,使他们继续向下执行。这就会造成数据的不一致,我们还是拿银行转账作为例子,我们还是从A账户向B账户转账500元,我们之前讨论过,这一过程分为三步,第一步是从A账户中减去500元,假如到这时线程就被stop了,那么这个线程就会释放它所取得锁,然后其他的线程继续执行,这样A账户就莫名其妙的少了500元而B账户也没有收到钱。这就是stop方法的不安全性。


二、suspend方法为什么被弃用


suspend被弃用的原因是因为它会造成死锁。suspend方法和stop方法不一样,它不会破换对象和强制释放锁,相反它会一直保持对锁的占有,一直到其他的线程调用resume方法,它才能继续向下执行。

假如有A,B两个线程,A线程在获得某个锁之后被suspend阻塞,这时A不能继续执行,线程B在或者相同的锁之后才能调用resume方法将A唤醒,但是此时的锁被A占有,B不能继续执行,也就不能及时的唤醒A,此时A,B两个线程都不能继续向下执行而形成了死锁。这就是suspend被弃用的原因。



三、如何正确的终止一个线程


[java]  view plain copy
  1. class MyThread extends Thread {  
  2.     public volatile boolean flag = true;  
  3.   
  4.     public void run() {  
  5.         while (flag) {  
  6.             System.out.println("线程正在执行");  
  7.             try {  
  8.                 Thread.sleep(500);  
  9.             } catch (InterruptedException e) {  
  10.                 // TODO Auto-generated catch block  
  11.                 e.printStackTrace();  
  12.             }  
  13.         }  
  14.         System.out.println("线程已终止");  
  15.     }  
  16. }  
  17.   
  18. public class Main {  
  19.   
  20.     public static void main(String[] args) {  
  21.         MyThread myThread = new MyThread();  
  22.         myThread.start();  
  23.         try {  
  24.             Thread.sleep(3000);  
  25.         } catch (InterruptedException e) {  
  26.             // TODO Auto-generated catch block  
  27.             e.printStackTrace();  
  28.         }  
  29.         myThread.flag = false;  
  30.         System.out.println("主线程结束!");  
  31.     }  
  32. }  

我们可以采用设置一个条件变量的方式,run方法中的while循环会不断的检测flag的值,在想要结束线程的地方将flag的值设置为false就可以啦!注意这里要将flag设置成volitale的,因为volitale可以保证数据的有效性,如果不设置话,可能会造成子线程多执行一次的错误,例如子线程将flag读到自己线程栈中,flag的值为true,此时子线程的交出执行权,操作系统将执行权交给了主线程,主线程执行flag=false;的操作,希望子线程不要再执行了,但是这一改变子线程是不能看到的,所以子线程还会再向下执行一次,然后重新读取flag的值的时候才会终止。


当然你也可以将对flag所有操作都封装在synchronized关键字修饰的方法中,实现互斥访问,也可以达到相同的效果。


四、如何正确的阻塞线程


如果想安全的阻塞线程,引入一个变量suspendRequested并在run方法安全的地方测试它,安全的地方是指该线程没有封锁其他线程需要的对象的地方。当线程发现如果变量已被设置,将会保持等待状态直到它再次被获得为止。


[java]  view plain copy
  1. public volatile boolean suspendRequested = false;  
  2.     private Lock suspendLock = new ReentrantLock();  
  3.     private Condition suspendCondition = suspendLock.newCondition();  
  4.   
  5.     public void run() {  
  6.   
  7.         while (true) {  
  8.             if (suspendRequested) {  
  9.                 suspendLock.lock();  
  10.                 try {  
  11.                     while (suspendRequested)  
  12.                         suspendCondition.await();  
  13.                 } catch (Exception e) {  
  14.   
  15.                 } finally {  
  16.                     suspendLock.unlock();  
  17.                 }  
  18.             }  
  19.         }  
  20.     }  
  21.   
  22.     public void requestSuspend() {  
  23.         suspendRequested = true;  
  24.     }  
  25.   
  26.     public void requestResume() {  
  27.         suspendRequested = false;  
  28.         suspendLock.lock();  
  29.         try {  
  30.             suspendCondition.signalAll();  
  31.         } catch (Exception e) {  
  32.             e.printStackTrace();  
  33.         } finally {  
  34.             suspendLock.unlock();  
  35.         }  
  36.     }  

初始的java版本中定义了一个stop方法来终止一个线程还定义了一个suspend方法来阻塞一个线程,直到另一个线程调用resume方法。这两个方法在Java SE 1.2之后就被弃用了,因为这两种方法都不安全,下面我们分别来讨论一下为什么不安全和应该怎样做才是安全的。


一、stop方法为什么不安全


其实stop方法天生就不安全,因为它在终止一个线程时会强制中断线程的执行,不管run方法是否执行完了,并且还会释放这个线程所持有的所有的锁对象。这一现象会被其它因为请求锁而阻塞的线程看到,使他们继续向下执行。这就会造成数据的不一致,我们还是拿银行转账作为例子,我们还是从A账户向B账户转账500元,我们之前讨论过,这一过程分为三步,第一步是从A账户中减去500元,假如到这时线程就被stop了,那么这个线程就会释放它所取得锁,然后其他的线程继续执行,这样A账户就莫名其妙的少了500元而B账户也没有收到钱。这就是stop方法的不安全性。


二、suspend方法为什么被弃用


suspend被弃用的原因是因为它会造成死锁。suspend方法和stop方法不一样,它不会破换对象和强制释放锁,相反它会一直保持对锁的占有,一直到其他的线程调用resume方法,它才能继续向下执行。

假如有A,B两个线程,A线程在获得某个锁之后被suspend阻塞,这时A不能继续执行,线程B在或者相同的锁之后才能调用resume方法将A唤醒,但是此时的锁被A占有,B不能继续执行,也就不能及时的唤醒A,此时A,B两个线程都不能继续向下执行而形成了死锁。这就是suspend被弃用的原因。



三、如何正确的终止一个线程


[java]  view plain copy
  1. class MyThread extends Thread {  
  2.     public volatile boolean flag = true;  
  3.   
  4.     public void run() {  
  5.         while (flag) {  
  6.             System.out.println("线程正在执行");  
  7.             try {  
  8.                 Thread.sleep(500);  
  9.             } catch (InterruptedException e) {  
  10.                 // TODO Auto-generated catch block  
  11.                 e.printStackTrace();  
  12.             }  
  13.         }  
  14.         System.out.println("线程已终止");  
  15.     }  
  16. }  
  17.   
  18. public class Main {  
  19.   
  20.     public static void main(String[] args) {  
  21.         MyThread myThread = new MyThread();  
  22.         myThread.start();  
  23.         try {  
  24.             Thread.sleep(3000);  
  25.         } catch (InterruptedException e) {  
  26.             // TODO Auto-generated catch block  
  27.             e.printStackTrace();  
  28.         }  
  29.         myThread.flag = false;  
  30.         System.out.println("主线程结束!");  
  31.     }  
  32. }  

我们可以采用设置一个条件变量的方式,run方法中的while循环会不断的检测flag的值,在想要结束线程的地方将flag的值设置为false就可以啦!注意这里要将flag设置成volitale的,因为volitale可以保证数据的有效性,如果不设置话,可能会造成子线程多执行一次的错误,例如子线程将flag读到自己线程栈中,flag的值为true,此时子线程的交出执行权,操作系统将执行权交给了主线程,主线程执行flag=false;的操作,希望子线程不要再执行了,但是这一改变子线程是不能看到的,所以子线程还会再向下执行一次,然后重新读取flag的值的时候才会终止。


当然你也可以将对flag所有操作都封装在synchronized关键字修饰的方法中,实现互斥访问,也可以达到相同的效果。


四、如何正确的阻塞线程


如果想安全的阻塞线程,引入一个变量suspendRequested并在run方法安全的地方测试它,安全的地方是指该线程没有封锁其他线程需要的对象的地方。当线程发现如果变量已被设置,将会保持等待状态直到它再次被获得为止。


[java]  view plain copy
  1. public volatile boolean suspendRequested = false;  
  2.     private Lock suspendLock = new ReentrantLock();  
  3.     private Condition suspendCondition = suspendLock.newCondition();  
  4.   
  5.     public void run() {  
  6.   
  7.         while (true) {  
  8.             if (suspendRequested) {  
  9.                 suspendLock.lock();  
  10.                 try {  
  11.                     while (suspendRequested)  
  12.                         suspendCondition.await();  
  13.                 } catch (Exception e) {  
  14.   
  15.                 } finally {  
  16.                     suspendLock.unlock();  
  17.                 }  
  18.             }  
  19.         }  
  20.     }  
  21.   
  22.     public void requestSuspend() {  
  23.         suspendRequested = true;  
  24.     }  
  25.   
  26.     public void requestResume() {  
  27.         suspendRequested = false;  
  28.         suspendLock.lock();  
  29.         try {  
  30.             suspendCondition.signalAll();  
  31.         } catch (Exception e) {  
  32.             e.printStackTrace();  
  33.         } finally {  
  34.             suspendLock.unlock();  
  35.         }  
  36.     }  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值