怎样合理的停止一个Java线程
在Java中,最好的停止线程的方式是使用中断 Interrupt,但是这仅仅是会通知到被终止的线程你该停止运行了”,被终止的线程自身拥有决定权(决定是否、以及何时停止),这依赖于请求停止方和被停止方都遵守一种约定好的编码规范。
任务和线程的启动很容易。在大多数时候,我们都会让它们运行直到结束,或者让它们自行停止。然而,有时候我们希望提前结束任务或线程,或许是因为用户取消了操作,或者服务需要被快速关闭,或者是运行超时或出错了。
要使任务和线程能安全、快速、可靠地停止下来,并不是一件容易的事。Java没有提供任何机制来安全地终止线程。但它提供了中断( Interruption)这是一种协作机制,能够使一个线程终止另一个线程的当前工作。
这种协作式的方法是必要的,我们很少希望某个任务、线程或服务立即停止,因为这种立即停止会使共享的数据结构处于不ー致的状态。相反,在编写任务和服务时可以使用一种协作的方式:当需要停止时,它们首先会清除当前正在执行的工作,然后再结束。这提供了更好的灵活性,因为任务本身的代码比发出取消请求的代码更清楚如何执行清除工作。
生命周期结束(End-of- Lifecycle)的问题会使任务、服务以及程序的设计和实现等过程变得复杂,而这个在程序设计中非常重要的要素却经常被忽略。一个在行为良好的软件与勉强运的软件之间的最主要区别就是,行为良好的软件能很完善地处理失败、关闭和取消等过程。
恢复中断线程两个方法
1.处理一个中断线程最好的方法优先选择向上抛出异常,使用throws InterrruptedException 标记方法,不采用try语句捕获异常,以便于该异常可以传递到顶层,让run方法可以捕获该异常。
void task() throws InterruptedException{
Thread.sleep(2000);
}
由于run方法无法再向外抛出checked Exception,只能使用try catch来处理异常,可以避免异常漏掉或吞掉的情况。
2.如果不想或无法传递InterruptedException,可以使用try catch ,但是应该再catch中重新设置线程中断状态,以便于下个循环时可以监测到,例如下方代码:
public void reInterrupt(){
try{
Thread.sleep(2000);
}catch(InterruptedException e){
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
注意:java在设计sleep函数时就规定,sleep函数一旦响应中断,就会清除线程的interrupt标记位。所以在循环内try catch捕获异常,就算在循环开始时使用Thread.currentThread().isInterrupted()检测中断,也是不能成功拦截,因为此时中断标记位已经被清除。所以try语句应该放在循环外。如果在循环内捕获中断,在catch里就应该使用Thread.currentThread.().interrupt()重新标记中断,并在循环开始时使用Thread. currentThread().isInterrupted()检测中断。
不好的停止线程的方法:
1.stop,suspend,resume,
2.用volatile设置boolean标记位。
stop:会使线程运行到一半突然停止,没办法保证完成一个基本操作,会导致产生脏数据
suspend,resume:它会使线程挂起,再恢复之前并不会释放锁,这样就很容易造成死锁(当它挂起时,其他线程需要获取这个锁时,就造成了死锁)。
用volatile设置boolean标记位
/**
*看着可行的volatile停止方法:
*/
public class WrongWayVolatile implements Runnable{
private volatile boolean canceled = false;
@Override
public void run(){
int num = 0;
try{
while(num <= 100000 && !canceled){
if(num % 100 == 0){
System.out.println(num + "是100的倍数");
}
num++;
Thread.sleep(1);
}
}catch(InterruptedException e){
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException{
WrongWayVolatile r = new WrongWayVolatile();
Thread thread = new Thread(r);
thread.start();
Thread.sleep(5000);
r.canceled = true;
}
}
/**
*当陷入阻塞的时候,volatile是无法停止线程的
*此例子中,生产者生产速度很快,消费者消费很慢,等阻塞队列满后,
*生产者会陷入等待,等待消费者进一步消费
*/
public class WrongWayVolatileCantStop{
public static void main(String[] args){
ArrayBlockingQueue storage = new ArrayBlockingQueue(10)
Producer producer = new Producer(storage);
Thread producerThread = new Thread(producer);
producerThread.start();
Thread.sleep(1000);
Consumer consumer = new Consumer(storage);
while(consumer.needMoreNums()){
System.out.println(consumer.storage.take() + "被消费了");
Thread.sleep(100);
}
System.out.println("消费者不需要更多信息了");
//一旦消费者不需要更多数据,我们应该让生产者也停下来,但是实际情况并没有停止
producer.canceled=true;
System.out.println(producer.canceled);
}
}
class Producer inplements Runnable{
public volatile boolean canceled = false;
BlockingQueue storage;
public Producer(BlockingQueue storage){
this.storage = storage;
}
@Override
public void run(){
int num = 0;
try{
while(num <= 100000 && !canceled){
if(num % 100 == 0){
storage.put(num);
System.out.println(num + "是100的倍数,被放到仓库中。");
}
num++;
Thread.sleep(1);
}
}catch(InterruptedException e){
e.printStackTrace();
}finally{
System.out.println("生产者停止运行。");
}
}
}
class Consumer{
BlockingQueue storage;
public Consumer(BlockingQueue storage){
this.storage = storage;
}
public boolean needMoreNums(){
if(Math.random()>0.95){
return fase;
}
return true;
}
}
/**
* 用中断来修复上方无限等待问题
*/
public class WrongWayVolatileFixed{
WrongWayVolatileFixed body = new WrongWayVolatileFixed();
public static void main(String[] args){
ArrayBlockingQueue storage = new ArrayBlockingQueue(10)
Producer producer = body.new Producer(storage);
Thread producerThread = new Thread(producer);
producerThread.start();
Thread.sleep(1000);
Consumer consumer = body.new Consumer(storage);
while(consumer.needMoreNums()){
System.out.println(consumer.storage.take() + "被消费了");
Thread.sleep(100);
}
System.out.println("消费者不需要更多信息了");
producerThread.interrupt();
System.out.println(producer.canceled);
}
class Producer inplements Runnable{
BlockingQueue storage;
public Producer(BlockingQueue storage){
this.storage = storage;
}
@Override
public void run(){
int num = 0;
try{
while(num <= 100000 && !Thread.currentThread().isInterrrupted()){
if(num % 100 == 0){
storage.put(num);
System.out.println(num + "是100的倍数,被放到仓库中。");
}
num++;
Thread.sleep(1);
}
}catch(InterruptedException e){
e.printStackTrace();
}finally{
System.out.println("生产者停止运行。");
}
}
}
class Consumer{
BlockingQueue storage;
public Consumer(BlockingQueue storage){
this.storage = storage;
}
public boolean needMoreNums(){
if(Math.random()>0.95){
return fase;
}
return true;
}
}
}