关于java:线程的中断

工作和线程的启动很容易。在大多数时候,咱们都会让它们运行直到完结,或者让它们自行进行。然而,有时候咱们心愿提前结束工作或线程,或者是因为用户勾销了操作,或者应用程序须要被疾速闭要使工作和线程能安仝、疾速、牢靠地停止下来,并不是一件容易的事。Java的Thread类为咱们提供了stop()suspend()等进行挂起线程的办法,然而因为平安问题目前都已被弃用。Java并没有提供一种平安的抢占式办法来进行线程,但它提供了中断(Interruption),这是一种合作机制,采纳合作式的形式使一个线程终止另一个线程的当前工作。 这种合作式的办法是必要的,咱们很少心愿某个工作、线程或服务立刻进行,因为这种立刻进行会使共享的数据结构处于不统一的状态。相同,在编写工作和服务时能够应用一种合作的形式:当须要进行时,它们首先会革除以后正在执行的工作,而后再完结。这提供了更好的灵活性,因为工作自身的代码比收回勾销申请的代码更分明如何执行革除工作。 生命周期完结(End-of-Lifecycle)的问题会使工作、服务以及程序的设计和实现等过程变得复杂,而这个在程序设计中十分重要的因素却常常被疏忽。一个在行为良好的软件与勉强运行的软件之间的最次要区别就是,行为良好的软件能很欠缺地解决失败、敞开和勾销等过程。如何设计一种合作机制,让线程能够平安的中断呢?咱们能够设置一个勾销标记,在工作线程会被中断的中央去查看这个标记,当查看到这个中断标记被设置为已勾销时,工作线程便开始做勾销工作。

public class CancelableThread implements Runnable {
    
    // 线程勾销标记,volatile润饰,保障内存可见性
    private volatile boolean isCanceled = false;
    
    @Override
    public void run() {
        while (!isCanceled) {//在工作线程中轮询检测这个勾销标记
            System.out.println("The current thread is doing something...");
            System.out.println(Thread.currentThread().getName() + " cancel flag is " + isCanceled);
        }
        // 当勾销标记被设置为true,执行以下代码,能够做一些勾销工作
        System.out.println(Thread.currentThread().getName() + "The current thread Has been cancelled");
    }

    private void cancel() {
        isCanceled = true;
    }
}
public class MainTest {
    public static void main(String[] args) throws Exception {
        CancelableThread cancelableThread = new CancelableThread();
        new Thread(cancelableThread).start();
        try {
            Thread.sleep(100);
        } finally {
            // 设置标记位为true,来中断线程  
      cancelableThread.cancel();
        }
    }
}
  • 打印后果
Thread-0 cancel flag is false
The current thread is doing something...
Thread-0 cancel flag is false
The current thread is doing something...
Thread-0 cancel flag is false
The current thread is doing something...
Thread-0 cancel flag is false
The current thread is doing something...
Thread-0 cancel flag is true
Thread-0The current thread Has been cancelled 

总结一下下面的例子,这个例子有个缺点,run办法如果这样写:

@Override
public void run() {
    while(!isCanceled){
        try {
            // 这里相当于线程被挂起了
            Thread.sleep(10000);
        }catch (InterruptedException e){
            // 这里用的是isInterrupted办法,标记位被革除了
        }
    }
    // 检测到中断标记,跳出循环
}

如果以后线程执行到了sleep办法,线程被挂起了,无论标记位isCanceled怎么变,基本跳不出循环,这样很可能导致和咱们预期的后果不统一,所以不倡议应用自定义的标记位来控制线程的中断,该当用上面的办法。

Thread类为咱们提供了三个与线程中断相干的办法,来实现上述机制。这三个办法别离是:

public void interrupt() {
  //...  省略相干代码
  interrupt0();           // Just to set the interrupt flag       
  //...  省略相干代码
}
public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}
public boolean isInterrupted() {
    return isInterrupted(false);
}
  • interrupt()办法次要用来设置中断标记位;如果此线程在调用wait办法或joinsleep办法阻塞时,那么它的中断状态将被革除(也就是线程不会理睬中断标记位),并且会收到一个InterruptedException异样。
  • 动态的interrupted()办法用来测试以后线程是否被中断,调用此办法会革除线程的中断状态。如果线程已被中断,调用此办法返回false;
  • isInterrupted()办法用来测试以后线程是否被中断,然而不会革除线程的中断状态。
public class InterruptTest {
    static class InnerThread extends Thread{
        @Override
        public void run() {
            while(!isInterrupted()){
                System.out.println(Thread.currentThread().getName()+" cancle flag is "+isInterrupted());
                try {
                    Thread.sleep(100);
                }catch (InterruptedException e){
                    e.printStackTrace();
                    // 抛出InterruptedException,中断标记位被革除,再次调用 interrupt();
                    interrupt();
                }
            }
            System.out.println(Thread.currentThread().getName()+" cancle flag is "+isInterrupted());
        }
    }


    public static void main(String[] args) {
        InnerThread innerThread = new InnerThread();
        innerThread.start();
        try {
            Thread.sleep(1000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        innerThread.interrupt();
//        InnerThread innerThread2 = new InnerThread();
//        innerThread2.start();
//        innerThread2.interrupt();
    }
}
  • 打印后果
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at InterruptTest$InnerThread.run(InterruptTest.java:13)
Thread-0 cancle flag is true

文章出处(内容有所批改):https://www.cnblogs.com/perki…

总结一下:

  1. 个别状况下不倡议应用自定义的中断标记位,起因下面阐明了
  2. interruptisInterrupted办法配合应用,能解决绝大多数的中断业务
  3. 在调用waitsleepjoin等阻塞或者挂起办法的时候,会抛出中断异样,为什么这么设计?这是为了在线程因为某种原因被挂起后,后续的业务如果说还没执行完,强制中断线程会导致不可意料的结果(比如说文件写入了一半),如果就是想中断线程,能够在catch块中再次调用interrupt办法
  4. 动态的interrupted()isInterrupted()办法都是调用的private native boolean isInterrupted(boolean ClearInterrupted)依据传入的ClearInterrupted的值,来判断是否要革除中断标记位
  5. wait、notify、notifyAll为什么被定义在了Object类中?这些都跟锁无关,锁是属于对象的

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理