java 线程中断机制

来源:互联网 发布:大连理工大学知乎 编辑:程序博客网 时间:2024/05/24 15:36


     上一篇文章我们了解过了java有关线程的基本概念,有线程的属性,线程可能处于的状态,还有线程的两种创建的方式,最后还说了一个关键字synchronized,解决了高并发导致数据内容不一致问题,本篇文章就介绍线程的中断机制。
     首先我们需要知道,java中的每个对象都是有内部对象锁的,其实每个java对象不止包含一个内部对象锁,还包含了一个等待队列和一个条件队列。

public synchronized void showName(){    ************}


     这样的一个被synchronized关键字修饰的方法,如果已经有线程获得了该对象的锁,再有线程过来就会被阻塞并丢入等待队列中。

private int count;public synchronized void showName(){    if(count==0){        wait();    }}public synchronized void setName(){    if(count==0){        notifyAll();    }}


     而在上述的代码段中,出现了两个方法:wait和notifyAll。这两个方法是成对出现的,wait主要作用是将当前线程阻塞并且置入条件队列上,notifyAll主要用于解放条件队列上的所有线程,使他们可以再次竞争处理机的调度以便运行。
     小结一下,以上就比较了等待队列和条件队列的作用和区别。等待队列往往是因为想要申请的锁已经被别的线程占有了,需要置入等待队列等待占有锁的线程释放锁,然后接受处理机调度,被选中后方能获得锁并运行。而条件队列却是一个已经获得锁的线程在执行过程中发现自己继续运行的条件不满足(无法获得某些需要的资源),程序无法继续执行下去了,于是调用wait方法阻塞自己并释放外部对象锁,置入条件队列。
     一旦占有某类资源的线程释放该资源之后,调用notifyAll方法释放所有在条件队列上等待的线程,使他们重新接受处理机的挑选。如果被处理机选中运行,就会从条件队列出队,占有对象锁,开始运行。需要注意的是:wait和notifyAll方法只能在synchronized修饰的代码块中调用,否则就会抛出异常。其实原因也很简单,wait必须指定将当前的线程挂在什么对象的条件队列上,notifAll必须被指定释放什么对象上的所有条件队列中的线程。synchronized关键字是针对某个对象加锁的,在其代码块中调用上述两种方法就默认操作被锁住的对象的条件队列。

一、如何中断
     接下来我们看看线程的中断,有人会问:每个线程执行自己的程序,直到程序的最后一条代码,执行完成之后就会自动的死亡,为什么要使用中断机制来主动的让他死亡呢?其实在我们的程序中往往需要在程序执行的过程中显式的就是线程的运行。比如:有些线程永远不会执行结束,比如你打开浏览器访问网站,如果没有中断线程机制,你永远都不会等到线程运行结束的那一刻。只能通过显式的中断来结束一个线程的运行。
     Thread类中定义了如下几个关于中断的方法:

public boolean isInterrupted()public void interrupt()public static boolean interrupted()


          三者方法名类似,但是却具有完全不同的操作。isInterrupted() 方法其实就是判断当前线程是否被中断,interrupt() 方法表示将线程的中断状态置为true,interrupted() 方法是一个静态的方法,测试当前线程的中断状态是否为true,和第一种方法功能一样,但是这个方法有一个额外的操作:会将调用此方法的线程的中断状态位置为false。就是顺便将中断位置为false。

二、线程的不同的状态对中断的反应
     我们知道线程的状态主要有,New,Runnable,Blocked,waiting,Terminated。下面我们看看每种状态下的线程对于中断的应对是怎么样的。
     首先是New状态,New状态下的线程并没有处于运行状态,这个状态下,中断操作对该线程没有任何影响。中断标志位也不会被置为true。其实可以理解的,一个都没有机会运行的线程,本来就是处于类似于中断的状态下。这种状态和Terminated状态下的线程对于中断的反应是一样的。
     下面我们看看Runnable状态下的线程是怎样的反应。一个正在运行的线程,如果调用了interrupt方法,只会将此线程的中断表示为置为true,一旦该线程在某个阶段检测自己的中断表示位,发现如果是true就会中断线程。但是在没有自我检测的期间,线程是可以正常运行的。

public class Test_thread extends Thread{    public void run(){        while(true) {        }    }}public class Test_Class {    public static void main(String[] args){        Thread thread = new Test_thread();        thread.start();        thread.interrupt();        try{            Thread.sleep(1000);        } catch (InterruptedException e){        };        System.out.println(thread.isInterrupted());    }}/*输出:true*/


     如上述的程序运行结果所示,一个将正在运行的线程interrupt,虽然标示位已经被更改,但是线程不会因此被中断。

     接下来看看Blocked状态下线程应对中断的反应。本篇文章的开头已经说过了,Bloked状态是由于申请某个对象的锁失败之后被放置在了该对象的等待队列上,这个队列上的线程都是Blocked状态的。在这种状态下,调用interrupt() 方法,一样的只会将该线程的中断标志位置为true但是不会导致该线程退出等待队列。

public class Test_Class {    private static Object count = new Object();    public static void main(String[] args){        try {            show();        }catch (InterruptedException e){}    }    public static void show() throws InterruptedException{        synchronized (count){            Thread thread = new myThread();            thread.start();            thread.interrupt();            System.out.println(thread.isInterrupted());            System.out.println(thread.getState());            thread.join();        }    }    public static class myThread extends Thread{        public void run(){            synchronized (count){                while(!Thread.currentThread().isInterrupted()){                }                System.out.println(this.toString());            }        }    }}/*输出结果:trueBLOCKED*/


          我们在main函数中调用show方法(这是一个线程),获取count的锁,在show方法内部创建一个线程,start之后执行run方法时没有能够获得count的锁,接着中断该线程,于是被阻塞在等待队列中。从输出的结果可以看出,一个状态是Blocked的线程在被中断之后,除了中断状态位被修改之后,其余没有什么变化。

          最后,我们看线程的waiting状态,一个处于waiting状态的线程,执行方法interrupt,会导致抛出Inerruption异常,即不允许一个处于waiting状态的线程被中断。但是,有一点是:此异常被抛出之后,会清空中断标志位。

public class Test_thread extends Thread{    public void run(){        try {            Thread.sleep(1000);        }catch (InterruptedException e){            System.out.println(0);        }    }}public class Test_Class {    public static void main(String[] args){        Thread thread = new Test_thread();        thread.start();        try{            Thread.sleep(100);        }catch (InterruptedException e){            System.out.println(1);        }        thread.interrupt();        try {            thread.join();        }catch(InterruptedException e){            System.out.println(3);        }    }}


          启动新线程,调用sleep方法使该线程处于waiting状态,与此同时,主线程调用子线程的interrupt方法,中断该线程,于是处于waiting状态的线程抛出异常,被捕获之后输出0;

          本篇文章结束,大家需要知道的就是在线程处于不同的状态下,遇到中断的处理方式是不一样的,对于是否就行处理其实还是取决与我们程序员写的代码。文章依然是自己阅读别人博客和自己总结实践得来,如果有错误,望大家指出来。感谢!

0 0