java的如何停止工作_如何中止Java中的线程

如何正确的中止一个运行中的线程

中止线程的方法大致分两种

通过Thread.interrupt()

通过线程间的共享标记变量

通过interrupt()来中止一个线程

由于被中止的线程可能处于不同的状态,被终止的线程需要自行检测是否有人发信号给他,通知应该停止工作。

检测是否有人发信号的方法有两种

Thread.currentThread().isInterrupted() 返回当前线程上的标识位。推荐使用,该方法调用后不会修改标识;

Thread.interrupted() 返回当前线程上的标识位,同时调用后会立即把标识位修改位false。

终止一个runnable线程的最佳实践:

while(!Thread.currentThread().isInterrupted() && more work to do){

do more work

}

处于runnable状态的线程被interrupt

看一个例子,对于一个循环打印running的线程,我们在main线程中调用了interrupt()方法,但是线程并没有按照我们预想的那样停下来,这样的代码并不是按照最佳实践的方式来运行的,我们没有把线程是否有接受到interrupt的信号放到while的判断去。

public class InterruptRunning {

public static void main(String[] args) {

Thread thread = new Thread(() -> {

while (true) {

System.out.println("running");

}

});

thread.start();

try {

Thread.sleep(2);

} catch (InterruptedException e) {

e.printStackTrace();

}

thread.interrupt();

System.out.println("Test End");

}

}

//输出结果

//一直持续不断的输出running ,并没有像期待的那样2ms之后输出Test End

以上的代码的正确写法应该是,可以看到结果是我们期待的

public class InterruptRunning {

public static void main(String[] args) {

Thread thread = new Thread(() -> {

while (!Thread.currentThread().isInterrupted()) {

System.out.println("running");

}

});

thread.start();

try {

Thread.sleep(2);

} catch (InterruptedException e) {

e.printStackTrace();

}

thread.interrupt();

System.out.println("Test End");

}

}

//输出结果

running

running

running

running

running

running

running

running

Test End

处于blocked/waiting/timed waiting状态的线程被interrupt

如果一个线程处于了阻塞状态(如线程调用了thread.sleep、thread.join、thread.wait、1.5中的condition.await、以及可中断的通道上的 I/O 操作方法后可进入阻塞状态),则在线程在检查中断标示时如果发现中断标示为true,则会在这些阻塞方法(sleep、join、wait、1.5中的condition.await及可中断的通道上的 I/O 操作方法)调用处抛出InterruptedException异常,并且在抛出异常后立即将线程的中断标示位清除,即重新设置为false。抛出异常是为了线程从阻塞状态醒过来,并在结束线程前让程序员有足够的时间来处理中断请求。

即先后做了三件事

使线程状态由阻塞状态变为runnable

抛出InterruptedException异常

如果InterruptedException异常被捕获,会立即将中断标识由true设置为false

举个例子,我们在循环的时候通过中断标识位来决定是否循环,并且将try catch代码块置于while循环内部。此时中断产生的异常会先被try catch捕获,然后中断标识位会被设置位false;while循环不会中止,线程依旧执行。

在main线程中调用standardInterrupt.interrupt();尝试中止中止StandardInterrupt线程,线程的输出内容说明了其捕获到了异常,但是检查标识发现值位false说明已经被重置了,于是可以看到while循环条件满足,线程不会停止,依旧运行下去。

public class StandardInterrupt extends Thread{

public static void main(String[] args) throws InterruptedException {

StandardInterrupt standardInterrupt = new StandardInterrupt();

System.out.println("main thread --> start running");

standardInterrupt.start();

Thread.sleep(3000);

System.out.println("main thread --> send signal");

standardInterrupt.interrupt();

Thread.sleep(3000);

System.out.println("main thread --> stop application");

}

public void run(){

while(!Thread.currentThread().isInterrupted()){

System.out.println("active thread --> i am working");

try {

Thread.sleep(1000);

}catch (InterruptedException e){

System.out.println("active thread --> detective interrupt");

System.out.println("active thread --> check the signal: " +Thread.currentThread().isInterrupted());

}

}

System.out.println("active thread --> finish my working");

}

}

//输出

main thread --> start running

active thread --> i am working

active thread --> i am working

active thread --> i am working

main thread --> send signal

active thread --> detective interrupt

active thread --> check the signal: false

main thread --> stop application

active thread --> i am working

active thread --> i am working

active thread --> i am working

......

我们再稍作修改如上的代码,在捕获异常之后,主动的再次触发一次interrupt(),这可以将被自动设置回false的中断标识位再次变为true。

这次可以看到active thread --> finish my working输出,因为while循环去检查标识的时候,Thread.currentThread().isInterrupted() = true;

public class StandardInterrupt extends Thread{

public static void main(String[] args) throws InterruptedException {

StandardInterrupt standardInterrupt = new StandardInterrupt();

System.out.println("main thread --> start running");

standardInterrupt.start();

Thread.sleep(3000);

System.out.println("main thread --> send signal");

standardInterrupt.interrupt();

Thread.sleep(3000);

System.out.println("main thread --> stop application");

}

public void run(){

while(!Thread.currentThread().isInterrupted()){

System.out.println("active thread --> i am working");

try {

Thread.sleep(1000);

}catch (InterruptedException e){

System.out.println("active thread --> detective interrupt");

System.out.println("active thread --> check the signal: " +Thread.currentThread().isInterrupted());

Thread.currentThread().interrupt();

}

}

System.out.println("active thread --> finish my working");

}

}

//输出

main thread --> start running

active thread --> i am working

active thread --> i am working

active thread --> i am working

main thread --> send signal

active thread --> detective interrupt

active thread --> check the signal: false

active thread --> finish my working

main thread --> stop application

......

另一种修改方式,是将try catch放在while循环外面,这样跳出while的原因是因为检测到异常。然后try catch捕获异常,修改标识位。

public class StandardInterrupt extends Thread {

public static void main(String[] args) {

StandardInterrupt standardInterrupt = new StandardInterrupt();

System.out.println("main thread --> start running");

standardInterrupt.start();

try {

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("main thread --> send signal");

standardInterrupt.interrupt();

try {

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("main thread --> stop application");

}

public void run() {

try {

while (!Thread.currentThread().isInterrupted()) {

System.out.println("active thread --> i am working");

Thread.sleep(1000);

}

} catch (InterruptedException e) {

System.out.println("active thread --> detective interrupt");

System.out.println("active thread --> check the signal: " + Thread.currentThread().isInterrupted());

}

System.out.println("active thread --> finish my working");

}

}

//输出

main thread --> start running

active thread --> i am working

active thread --> i am working

active thread --> i am working

main thread --> send signal

active thread --> detective interrupt

active thread --> check the signal: false

active thread --> finish my working

main thread --> stop application

......

总结一下对于要处理这种blocked状态的线程应该正确书写代码的格式

public void run() {

try {

...

/*

* 不管循环里是否调用过线程阻塞的方法如sleep、join、wait,这里还是需要加上

* !Thread.currentThread().isInterrupted()条件,虽然抛出异常后退出了循环,显

* 得用阻塞的情况下是多余的,但如果调用了阻塞方法但没有阻塞时,这样会更安全、更及时。

*/

while (!Thread.currentThread().isInterrupted()&& more work to do) {

do more work

}

} catch (InterruptedException e) {

//线程在wait或sleep期间被中断了

} finally {

//线程结束前做一些清理工作

}

}

或者

public void run() {

while (!Thread.currentThread().isInterrupted()&& more work to do) {

try {

...

sleep(delay);

} catch (InterruptedException e) {

Thread.currentThread().interrupt();//重新设置中断标示

}

}

}

处于blocked状态的线程

由synchronized,或者是由reentrantLock.lock()导致的锁是无法响应interrupt()方法的,他们只有在获取锁之后正常运行的时候才能响应。

reentrantLock.tryLock(long timeout, TimeUnit unit),reentrantLock.lockInterruptibly()方法所导致的blocked状态却是能正确响应interrupt()方法的,锁住的线程将抛出一个InterruptedException异常,这是一个非常有用的特性,因为它允许程序打破死锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值