我们先来了解下线程的几种状态:
新建状态(New):一个新的线程在被创建之后,在运行(或阻塞)之前的状态。
就绪状态(Runnable):当一个线程只要拥有CPU时间片就能运行的时候,这个线程就处于就绪状态。
阻塞状态(Blocked):线程还没有死亡,但是由于一些原因不能继续运行,但是还能回到就绪状态。
死亡状态(Dead):当一个线程的任务已经完成,即run方法已经正确返回,或者被中断,这个线程就处于死亡状态。
线程阻塞的原因:
调用了Thread.sleep()方法;
调用了wait(),线程被挂起;
线程在等待其他操作完成。例如正在等待输入;
线程在等待其他线程持有的锁。
线程中断
java Thread中通过interrupt()方法中断线程,文档中对这个方法的功能描述是:
Interrupts this thread.
但是,这个方法并不能轻而易举地中断一个线程。我们可以用下面的代码进行测试:
public class InterruptThread extends Thread { @Override public void run() { while(true){ System.out.println("执行中..."); } } public static void main(String[] args) { InterruptThread interruptThread = new InterruptThread(); interruptThread.start(); interruptThread.interrupt(); } }
InterruptThread继承了Thread,在执行中即使调用了interrupt()方法。控制台还是不断地在打印。
看来,interrupt()方法并没有成功的中断我们的线程。
其实可以这样来类比(注意,只是类比,实际情况并不完全是这样):Thread类中有一个boolean的标志域用来表示线程是否需要被中断,默认是false。interrupt()方法被调用之后,这个标志域就变成了true。当然,Thread类有一个interrupted()方法返回一个boolean值,返回的就是这个标志域的值。也就是说,其实interrupt()方法除了把这个标志域设定为true之后,其他什么也没干了。这就解释了上例中的线程为什么不能被正确地中断。所以,我们可以把SimpleThread类的run()方法改写成下面这个样子:
public class InterruptThread extends Thread { @Override public void run() { while(!interrupted()){ System.out.println("执行中..."); } } public static void main(String[] args) { InterruptThread interruptThread = new InterruptThread(); interruptThread.start(); try { Thread.sleep(1000);//cpu执行过快,interrupThread还来不及运行就被interrupt()了,所以让主线程休眠1s } catch (InterruptedException e) { e.printStackTrace(); } interruptThread.interrupt(); } }
再运行测试程序,就会发现,1秒后线程会停止打印,它确实是被中断了。
有时候,我们不仅仅想要中断一个像上面这样重复着做同一件事的线程。很多情况下,我们需要的是中断一个休眠中的线程。毕竟开始因为某种需要,让一个线程休眠一段很长的时间,后来你就后悔了,想中断它的休眠。这个时候,interrupt()方法就又有用武之地了。
比如:
public class InterruptThread extends Thread { @Override public void run() { System.out.println("I want to sleep, Do not bother me!"); try { Thread.sleep(2000); } catch (InterruptedException e) { // e.printStackTrace(); System.out.println("my god, somebody bothered me."); } System.out.println("Wake up!"); } public static void main(String[] args) { InterruptThread interruptThread = new InterruptThread(); interruptThread.start(); // interruptThread.interrupt(); } }
I want to sleep, Do not bother me! Wake up!
如果我们放开
// interruptThread.interrupt();的注释public class InterruptThread extends Thread { @Override public void run() { System.out.println("I want to sleep, Do not bother me!"); try { Thread.sleep(2000); } catch (InterruptedException e) { // e.printStackTrace(); System.out.println("my god, somebody bothered me."); } System.out.println("Wake up!"); } public static void main(String[] args) { InterruptThread interruptThread = new InterruptThread(); interruptThread.start(); interruptThread.interrupt(); } }
I want to sleep, Do not bother me! my god, somebody bothered me. Wake up!
一个人睡觉,不能保证不被打搅;同理,一个线程在休眠,不能保证不被中断。这就是为什么Thread的sleep()方法会可能抛出一个InterruptedException的原因。同样地,Thread类的join()方法,Object类的wait()方法,都可能抛出InterruptedException。
所以,如果想要中断一个正在sleep或者join或者wait的线程,调用线程的interrupt()方法即可,然后把在中断后需要的操作写进catch语句块里。