java中断机制
java 提供一个非抢占式的中断机制.它是java语言级提供的一种线程间协作机制.
抢占式与非抢占式
抢占式(Preemptive) 指任务之间可以不管互相是否在运行中状态,都可以将抢占对方的CPU计算时间.这种策略可以防止单一任务长时间占用CPU.
非抢占式(Nonpreemptive) 指任务之间不可互相打断运行中状态.Java采用这种策略,使得中断更灵活.(也更麻烦)
中断有什么用?
中断在取消与关闭线程的场景里用的最多.
想想不用中断我们是怎样关闭其余线程的.
public class Main {
private static volatile boolean cancelled = false;
public static void main(String [] args) throws InterruptedException {
MyRunnable myRunnable = new MyRunnable();
new Thread(myRunnable).start();
Thread.sleep(3000);
System.out.println("Change cancelled to true!");
cancelled = true;
}
static class MyRunnable implements Runnable{
public void run() {
while (!cancelled){
try {
doSomething();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Thread exit!");
}
}
}
通过一个全局变量作为线程开关标志,子线程定时检查该标志是否关闭来决定子线程的关闭与否.
但是这样的代码会有一个潜在的隐患—-doSomething()
方法有可能是阻塞方法(比如BlockingQueue.put()
或sleep()
),那子线程就有可能永远不会停止.这当然不是我们想要的.怎么办?
中断在这个时候就非常好用!有点类似于这里的cancelled
标志,中断状态通过以下方法来读取和控制:
public class Thread{
//中断线程
public void interrupt() {...}
//返回线程的中断状态
public boolean isInterrupted() {...}
//清除中断状态,并返回之前的值
public static boolean interrupted() {...}
}
而与我们自定义的全局变量标志不同的是,java阻塞库方法(比如方法BlockingQueue.put()
或sleep()
)在运行的过程中会检查中断状态(不同与我们在代码中的做法,检查中断是java语言级支持的功能,所以效率和稳定性都很高),万一发现线程被中断,就会结束进程运行并抛出InterruptedException
.
具体说久是如果上面的doSomething()
方法是Thread.sleep()
.当我们中断子线程的时候,sleep()会(几乎)立即停止运行并抛出异常!
但如果是非阻塞的方法,如果不显式地处理中断,线程的运行状态并不会发生改变!这个要千万注意! 比如
以下代码
public class Main {
private static volatile boolean cancelled = false;
public static void main(String [] args) throws InterruptedException {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
Thread.sleep(1000);
System.out.println("Change cancelled to true!");
thread.interrupt();
}
static class MyRunnable implements Runnable{
public void run() {
while (true){
doSomething();
}
}
private void doSomething(){
waiting();
System.out.println("I am running!");
}
private void waiting(){
Long count = 1000000000l;
while (count-- > 0);
}
}
}
输出:
子线程并不会对中断做出相应,继续执行.