记得以前初学Java时老是搞不清这几个方法,现在稍微记录一下。
这算是 Java:sleep、wait、notify、notifyAll这篇文章的姊妹篇吧^_^
阻塞
一条线程进入阻塞的状态,可能有几个比较常见原因:
- 该线程调用了Thread.sleep()方法
- 该线程,某个对象调用了wait()方法
- 该线程正在等待I/O
- 该线程调用其他线程正占有锁的同步方法/块
前两者和后两者有个重要的区别,前两者会抛出InterruptedException
,而后两者不会。
这里先说明一条和本文讨论内容无关的一个结论:前两者的阻塞,可以通过线程对象
调用interrupt方法进行中断,后两者不可以,其中I/O可以通过关闭底层资源中断阻塞,同步则可以利用ReentrantLock
,因为它的lockInterruptibly
方法同样会抛出InterruptedException
。
下面正式开始讨论标题中三个方法
interrupt
当调用Thread.interrupt()方法时,首先设置该线程的中断状态
,如果此时该线程正在调用Object类的wait()方法或者Thread类的sleep(long)方法过程中阻塞
,那么这些方法将会抛出InterruptedException
,并清除中断状态
。如果线程并没有阻塞,则不会清除中断状态。
看一个炒鸡简单的例子:
// DEMO1
public static void main(String... args) throws InterruptedException {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(600);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
Thread.sleep(400);
t.interrupt();
}
开启一条子线程,这条线程睡眠600ms,主线程400ms后就中断他,显然阻塞的子线程会被中断。
假设在线程没有阻塞时调用interrupt()
方法呢?
// DEMO2
public static void main(String... args) throws InterruptedException {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
long a = 0;
while (true) {
Thread.sleep(600);
for (int i = 0; i < 1000000; i++) {
for (int j = 0; j < 10000; j++) {
a++; // cost about 600ms
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
Thread.sleep(800);
t.interrupt();
}
开启一条子线程,这条线程睡眠600ms,主线程800ms后才中断他,子线程在800ms时并未阻塞,而是进行其他运算,这时子线程的中断状态
被设置,当下一次循环调用Thread.sleep(600)时,由于此时中断状态是被设置的,这时仍会抛出InterruptedException
。
结论是:某个会造成线程阻塞并能够抛出InterruptedException
的方法,在两种情况下会抛出异常,1、该方法正在执行时,中断状态被设置;2、中断状态被设置时,调用该方法。抛出异常后,中断状态
都会被清除
。
interrupted()与isInterrupted()
当你能理解上面的中断状态后再来看这两个方法就非常简单了!
// DEMO3
public static void main(String... args) throws InterruptedException {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
long a = 0;
while (!Thread.interrupted()) {
Thread.sleep(600);
for (int i = 0; i < 1000000; i++) {
for (int j = 0; j < 10000; j++) {
a++; // almost cost 600ms
}
}
}
System.out.println("exit by flag");
} catch (InterruptedException e) {
System.out.println("exit by exception");
e.printStackTrace();
}
System.out.println(Thread.currentThread().isInterrupted());
}
});
t.start();
Thread.sleep(800);
t.interrupt();
}
interrupted()和isInterrupted()都是用来检测当前线程是否处在中断状态
,调用完后前者会清除
中断状态,而后者则保留
中断状态。
回到上诉示例,一共有四种情况:
- 主线程睡眠400ms后调用interrupt(),此时子线程通过异常中断阻塞,最后打印false;
- 主线程睡眠800ms后调用interrupt(),此时子线程通过检测中断状态跳出循环,最后打印false。
将循环条件改为while(!Thread.currentThread().isInterrupted())
,1和2情况最后则打印true。
小结
- 线程阻塞有几种情况,不同阻塞的中断方法也不同;
- interrupt()用来设置线程的中断状态,能够抛出
InterruptedException
的阻塞方法+中断状态会抛出异常并清除线程的中断状态。 - interrupted()和isInterrupted()调用完后,前者会
清除
中断状态,而后者则保留
中断状态。