BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / java / #41451同步于 2015/6/8
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Java机器人发帖

InterruptedException是干什么用的?

nuanyangyang
2015/6/8镜像同步57 回复
Interrupt是什么意思? Java里,每个线程都有一个“interrupted”标志。如果调用这个线程的interrupt()方法,就会设置这个标志。这个标记的意思是建议该线程停止。 可以使用Thread.interrupted()静态方法检测当前线程是否被中断。如果检测到true,那么同时会清除当前线程的interrupt标记。 Java不推荐使用Thread.stop()来终止线程。推荐的做法是,时不时地检测interrupt()状态,并在尽可能短的时间内自行终止。 下面的程序每计算若干个数,就检测是否被中断。如果被中断了,返回部分结果。 package cn.byr.nuanyangyang.interr; class InterruptibleSumCalculator implements Runnable { private long low; private long high; private boolean finished = false; private long result = 0; public InterruptibleSumCalculator(long low, long high) { this.low = low; this.high = high; } @Override public void run() { final long CHUNK_SIZE = 1024; long s = 0; for (long i = low; i <= high; i += CHUNK_SIZE) { for (long j = i; j < i + CHUNK_SIZE; j++) { s += j; } if (Thread.interrupted()) { System.out.format("Sum calculation interrupted.\nCurrent i: %d, partial sum: %d\n", i, s); result = s; Thread.currentThread().interrupt(); // re-set the interrupted flag. // Maybe this calculator is part of a bigger job. // I'll come back to this issue later. return; } } finished = true; result = s; } public boolean isFinished() { return finished; } public long getResult() { return result; } } public class InterruptibleSum { public static void main(String[] args) throws InterruptedException { InterruptibleSumCalculator isc = new InterruptibleSumCalculator(0L, 100000000000L); Thread t = new Thread(isc); System.out.println("Start calculating..."); t.start(); System.out.println("Waiting for it to finish..."); t.join(1000); if(t.isAlive()) { System.out.println("Still alive. Try to interrupt it..."); t.interrupt(); System.out.println("Waiting for it to die..."); t.join(); } else { System.out.println("Already dead."); } System.out.format("Finished? %s\nResult: %d\n", isc.isFinished(), isc.getResult()); } } 很多标准库中的函数都会响应interrupt()函数。在被中断的时候,会抛出InterruptedException。 比如,Thread.sleep()。 package cn.byr.nuanyangyang.interr; public class InterruptibleSleep { public static void main(String[] args) throws InterruptedException { Thread sleeper = new Thread(() -> { try { Thread.sleep(1000); System.out.println("I woke up by myself."); } catch (InterruptedException e) { System.out.println("I was interrupted."); // Thread.currentThread().interrupt(); // I know it is the whole job, so no need to re-set the flag. } }); sleeper.start(); Thread.sleep(500); System.out.println("Main thread woke up."); sleeper.interrupt(); sleeper.join(); } } interrupt的意思是建议线程停止。所以,正确的处理方法是停止当前的作业。 一个典型的错误是直接将会抛出InterruptException的语句用try-catch括起来,这样编译可以通过,但却是错误的处理方法。比如下面这个程序: package cn.byr.nuanyangyang.interr; public class CounterWrong { public static void main(String[] args) throws InterruptedException { Thread counter = new Thread(() -> { int i = 0; while (true) { System.out.println(i); i++; try { Thread.sleep(1000); } catch (InterruptedException e) { // WRONG!!! e.printStackTrace(); } } }); counter.start(); Thread.sleep(3500); counter.interrupt(); counter.join(); } } 这样的程序根本终止不了,因为一interrupt,sleep就会抛出InterruptedException,同时还会清除interrupt标记。所以,程序运行的现象就是程序在3.5秒的时刻打印了一个异常,然后计数器继续工作。 0 1 2 3 java.lang.InterruptedException: sleep interrupted 4 at java.lang.Thread.sleep(Native Method) at cn.byr.nuanyangyang.interr.CounterWrong.lambda$0(CounterWrong.java:11) at cn.byr.nuanyangyang.interr.CounterWrong$$Lambda$1/2003749087.run(Unknown Source) at java.lang.Thread.run(Thread.java:745) 5 6 正确的做法是停止运行 package cn.byr.nuanyangyang.interr; public class CounterRight { public static void main(String[] args) throws InterruptedException { Thread counter = new Thread(() -> { int i = 0; try { while (true) { System.out.println(i); i++; Thread.sleep(1000); } } catch (InterruptedException e) { System.out.println("Counter interrupted. Stop."); // Thread.currentThread().interrupt(); // I know it is the whole job, so no need to re-set the flag. } }); counter.start(); Thread.sleep(3500); counter.interrupt(); counter.join(); } } 有时候,你做的是一个函数,而不是一个完整的Thread或者Runnable。这种情况,可以让函数抛出InterruptedException,而不是在函数内部处理。这等于把处理InterruptedException的责任抛给了调用者。以原来的计算器为例,如果是一个函数而不是Runnable,那么应该这样写: package cn.byr.nuanyangyang.interr; public class InterruptibleSumFunc { public static long sum(long low, long high) throws InterruptedException { final long CHUNK_SIZE = 1024; long s = 0; for (long i = low; i <= high; i += CHUNK_SIZE) { for (long j = i; j < i + CHUNK_SIZE; j++) { s += j; } if (Thread.interrupted()) { throw new InterruptedException(String.format("Sum calculation interrupted. Current i: %d, partial sum: %d", i, s)); } } return s; } public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { try { long result = sum(0L, 1000000000L); System.out.println("Finished. Result is " + result); } catch (InterruptedException e) { System.out.println("Interrupted."); // Thread.currentThread().interrupt(); // I know it is the whole job, so no need to re-set the flag. } }); System.out.println("Start calculating..."); t.start(); Thread.sleep(1000); t.interrupt(); t.join(); } } 有时候,你想要把处理异常的责任抛给调用者,但由于要实现某种接口的原因,你不能抛出InterruptedException异常。通常是因为实现了Runnable接口。这种情况,可以在捕获异常的时候重新设置interrupted标志,这样,调用着紧接着检查Thread.interrupted()的时候,还会接受到这个标志。同时,随后的标准库函数,如sleep, wait等,也会因为这个标志而立即终止。 建议:如果你是写一个可重用的Runnable作业,那么一旦检测到被中断(截获InterruptedException,或者Thread.isinterrupted()为true),应该在Runnable.run()退出之前,重新设置interrupted标志。因为你永远不知道是不是有人在把你的作业组合到更大的作业里去。 package cn.byr.nuanyangyang.interr; class SleepJob implements Runnable { @Override public void run() { try { System.out.println("Started sleeping."); Thread.sleep(1000); System.out.println("Finished sleeping."); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // Re-set the interrupted flag. } } } public class MultiSleep { public static void main(String[] args) throws InterruptedException { Thread sleeperThread = new Thread(() -> { Runnable job1 = new SleepJob(); Runnable job2 = new SleepJob(); Runnable job3 = new SleepJob(); job1.run(); if (Thread.interrupted()) { System.out.println("Interrupted in job1. Stop."); return; } job2.run(); if (Thread.interrupted()) { System.out.println("Interrupted in job2. Stop."); return; } job3.run(); if (Thread.interrupted()) { System.out.println("Interrupted in job3. Stop."); return; } }); sleeperThread.start(); Thread.sleep(1500); sleeperThread.interrupt(); sleeperThread.join(); } }
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
lixing机器人#1 · 2015/6/8
先赞后看。
Monologue机器人#2 · 2015/6/8
明白了~~ 之前的代码的问题在于发生中断异常之后并没有真正的中断线程 这样理解对吗?
nuanyangyang机器人#3 · 2015/6/8
【 在 Monologue 的大作中提到: 】 : 明白了~~ : 之前的代码的问题在于发生中断异常之后并没有真正的中断线程 : 这样理解对吗? 嗯。是的。如果直接catch然后e.printStackTrace(),意思是“我已经处理了这个异常,现在已经恢复正常了”(当然实际上没有“恢复正常”),所以线程不会中断。
icyfox机器人#4 · 2015/6/8
我勒个去
dss886机器人#5 · 2015/6/8
我擦,我终于明白为什么Thread.sleep()需要try-catch了 【 在 nuanyangyang (暖羊羊) 的大作中提到: 】 : Interrupt是什么意思? : Java里,每个线程都有一个“interrupted”标志。如果调用这个线程的interrupt()方法,就会设置这个标志。这个标记的意思是建议该线程停止。 : 可以使用Thread.interrupted()静态方法检测当前线程是否被中断。如果检测到true,那么同时会清除当前线程的interrupt标记。 : ...................
NotGoodGuy机器人#6 · 2015/6/8
peakmuma机器人#7 · 2015/6/8
前排,先马再看
fuxuemingzhu机器人#8 · 2015/6/8
前排,学习学习~~~
dongqing机器人#9 · 2015/6/8
先顶,以后看