Java<优雅地理解线程interrupt、isInterrupted、interrupted>

来源:互联网 发布:常熟天下淘宝 编辑:程序博客网 时间:2024/06/05 14:14

1.什么是线程的中断

线程中断可以简单地理解为线程(Thread)有一个属性叫做“中断”,可以通过调用很多方法来查看该线程的这个属性的状态(TRUE、FALSE)

2.我调用这些方法的之后线程会立即中断么?

来看个例子:

public class MyThread extends Thread {    @Override    public void run() {        super.run();        for (int i = 0; i < 50000; i++) {            System.out.println(i);        }    }}
public class Run {    public static void main(String[] args) throws InterruptedException {        MyThread thread = new MyThread();        thread.start();        Thread.sleep(1000);        //调用线程Mythread的实例的中断方法        thread.interrupt();   }}

在main线程挂起1秒之后,然后main线程给Mythread的实例(这里简写为MTH)发送了一个消息,告诉他“你把你的中断属性设置为TRUE”,但是MTH收到了这个消息之后只是单存地把这个自己的属性设置为TRUE,”然后并不中断自己”。

控制台打印1-49999:

49993499944999549996499974999849999Process finished with exit code 0

到这里答案已经很明显了,线程并不会因为简单地收到了其他线程的消息而中断自己

!!!如果要中断自己还要有附加操作

这些额外的操作是什么?

//Interrupted的经典使用代码        public void run(){                try{                     ....                     while(!Thread.currentThread().isInterrupted()&& more work to do){                            // do more work;                     }                }catch(InterruptedException e){                            // thread was interrupted during sleep or wait                }                finally{                           // cleanup, if required                }        }    

Thread.currentThread().isInterrupted()可以用来检测当前线程的中断属性
while循环有一个决定因素就是需要不停的检查自己的中断状态。当外部线程调用该线程的interrupt 时,使得中断状态置位即变为true。这是该线程将终止循环,不在执行循环中的do more work了。

3.interrupt()

这个方法是线程的一个内部方法,是由其他线程调用的,比如我在main方法中让main线程来调用MTH的interrupt()方法.
比如下面这个代码:

public class Run {    public static void main(String[] args) throws InterruptedException {        MyThread thread = new MyThread();        thread.start();        Thread.sleep(1000);        //调用线程Mythread的实例的中断方法        thread.interrupt();   }}

这段代码会将MTH(如果你忘记了声明是MTH,网上翻)的中断属性为TRUE,仅此而已。

引申一下:

 public static void main(String[] args) throws InterruptedException {        Thread.currentThread().interrupt();}

如果我在main方法里调用了上面这行代码,会怎么样?
答案是:main线程的中断位会被设置为TRUE

4.isInterrupted()

这个方法主要用来获取当前线程的“中断”属性的状态,是TRUE还是FALSE。仅此而已。

套用上面的代码:

  public static void main(String[] args) throws InterruptedException {        Thread.currentThread().interrupt();        System.out.println("isInterrupted " +                Thread.currentThread().isInterrupted());}

这里有一个点需要注意:在上面的代码中
Thread.currentThread()得到的是当前正在执行main方法的线程(也就是main线程)

控制台会输出什么?

TRUE

线程默认的中断位是FALSE,main线程调用了interrupt()之后,其中断位为TRUE,
isInterrupted()方法获取到的当然是TRUE

5.interrupted()

调用该线程的方法的中断位会被重置为FALSE

 Thread.currentThread().interrupt();        System.out.println("isInterrupted " + Thread.currentThread().isInterrupted());        new MyThread().interrupted();//因为是Main线程执行这个方法,这个方法内部会把正在执行他的线程的中断标志位设置为false        System.out.println("isInterrupted " + Thread.currentThread().isInterrupted());

以上的代码的执行的结果是:

isInterrupted trueisInterrupted false

原因很简单,Thread.currentThread().isInterrupted()方法执行之后main线程的中断位会被置为TRUE;
之后new MyThread().interrupted()执行,那么这行代码是谁在执行?当然是main线程在执行,也就是说执行interrupted()方法的线程是main。之前说了谁执行了interrupted()方法谁的中断位就会被置为FALSE,那现在main的中断位自然就会被置为FALSE了。

上面的话可能有点绕口,如果你不理解这段话,请看下面这个小例子:

public class DemoThread {    static class MThread extends Thread {        public MThread() {            System.out.println("执行Mthread的线程是  :" + Thread.currentThread().getName());        }    }    public static void main(String[] args) {        new MThread();    }}

这时候控制台的输出是:

执行Mthread的线程是  :main

因为是main线程创建了子线程,自然子线程的构造函数是被main线程调用的。
如果你还看不懂,请反复看,这很关键。

如果我要修改main线程的中断位,我只用通过new MyThread().interrupted()方法么?答案是否定的,下面的代码也可做到

public class Run {    public static void main(String[] args) throws InterruptedException {        Thread.currentThread().interrupt();//false--->TRUE        System.out.println("isInterrupted " + Thread.currentThread().isInterrupted());        Thread.interrupted();//true-->false        System.out.println("isInterrupted " + Thread.currentThread().isInterrupted());    }}

* interrupted()的返回值:*
interrupted()会将标志位被自己置为之前的状态作为返回值然后,在置位标志位,看下面的代码:

public class Run {    public static void main(String[] args) throws InterruptedException {        Thread.currentThread().interrupt();        System.out.println(Thread.currentThread().interrupted());        System.out.println(Thread.currentThread().isInterrupted());    }}

控制台的输出是:

truefalse

因为Thread.currentThread().interrupt();将main线程置为了TRUE(FALSE->TRUE),然后System.out.println(Thread.currentThread().interrupted());将TRUE返回,然后在将中断位修改为FALSE,最后Thread.currentThread().isInterrupted()返回的就是FALSE了。

6.sleep() & interrupt()

如果线程MTH线程循环调用了sleep(10000),main线程想要让mth放弃Sleep状态,然后抛出异常

public class FindDiffInArray {    static class BusyThread extends Thread {        @Override        public void run() {            System.out.println("我是无相关线程");        }    }    static class MTH extends Thread {        @Override        public void run() {            try {                System.out.println("RUN begin");                Thread.sleep(2000000);                System.out.println("RUN end");            } catch (InterruptedException e) {                System.out.println("在沉睡中被停止 " + this.isInterrupted());                e.printStackTrace();            }        }    }    public static void main(String[] args) throws InterruptedException {        MTH mth = new MTH();        mth.start();        Thread.sleep(200);        mth.interrupt();        System.out.println(mth.isInterrupted());    }}

控制台的输出:

RUN beginjava.lang.InterruptedException: sleep interrupted    at java.lang.Thread.sleep(Native Method)    at FindDiffInArray$MTH.run(FindDiffInArray.java:28)在沉睡中被停止 falsefalse

可以看到mth.interrupt();被执行之前mth一直在循环执行sleep()函数,接着是接收到中断信号,但是由于此时mth没有占用CPU时间片,没有占用CPU运行的线程是不可能给自己的中断状态置位的。这就会产生一个InterruptedException异常。
但是抛出异常之后,mth的中断位并没有被置为TRUE(从控制台的打印信息来看)

7.join() & interrupt().

当线程以join()等待其他线程结束时,当它被调用interrupt(),它与sleep()时一样, 会马上跳到catch块里.
注意,是对谁调用interrupt()方法,一定是调用被阻塞线程的interrupt方法.如在线程a中调用来线程t.join().则a会等t执行完后在执行t.join后的代码,当在线程b中调用来 a.interrupt()方法,则会抛出InterruptedException,当然join()也就被取消了。

8. wait() & interrupt()

线程A调用了wait()进入了等待状态,也可以用interrupt()取消.
不过这时候要小心锁定的问题.线程在进入等待区,会把锁定解除,当对等待中的线程调用interrupt()时,会先重新获取锁定,再抛出异常.在获取锁定之前,是无法抛出异常的.

阅读全文
0 0
原创粉丝点击