第21章 – 并发 – 线程阻塞,线程中断,何时以及如何响应中断
第21章 – 并发 – 线程阻塞,线程中断,何时以及如何响应中断
1.在线程阻塞时中断
线程的4中状态:
(1) 新建(new):线程被创建时,它只会短暂的处于这种状态.
此时线程已经分配了必须的系统资源,并执行了初始化.
此刻线程已经有资格获取CPU时间,之后调度器将把这个线程转变为可运行状态
或阻塞状态
(2) 就绪(Runnable):在这种状态下,只要调度器把时间片分配给线程,线程就可以运行.
也就是说,在任意时刻线程可以运行也可以不运行.只要调度器能分配时间片给线程,
它就可以运行,这不同于阻塞和死亡状态.
(3) 阻塞(Blocked): 线程能够运行,但有某个条件阻止它运行.当线程处于阻塞状态时,
调度器将忽略该线程,不会分配给线程任何CPU时间.直到线程重新进入了就绪状态,
它才有可能执行操作.
(4) 处于死亡或终止状态的线程将不再是可调度的,并且也不会得到CPU时间,它的任务
已结束,或不再是可运行的.任务死亡的通常方式是从run()方法返回,但任务的线程
还可以被中断.
任务进入阻塞状态的4个原因:
(1) 通过调用sleep(milliseconds)使任务进入休眠状态,此时,任务在指定的时间内不会运行
(2) 通过调用wait()是线程挂起,直到线程得到了notify()或notifyAll()消息(或signal(),
signalAll()消息),线程才会进入就绪状态
(3)任务在等待某个输入/输出完成
(4) 任务视图在某个对象上调用其同步控制方法,但是对象的锁不可用,因为另一个任务已经
获取了锁.
另,在当前现场上调用其他线程的join()方法时,也会引起当前线程阻塞(Java并发编程实践(童云兰等译)P115).
2. sleep()造成的线程阻塞可以中断,而IO和synchronized造成的阻塞不可中断(即不能响应中断请求)
中断 不可中断阻塞 的方法: 关闭在其上发生阻塞的底层资源.比如 IO阻塞时不可中断,
在关闭这个IO时便可以中断.
nio类库中的IO阻塞时会自动响应中断.
在ReentrantLock上阻塞的任务具备可被中断的能力.
3. 线程的interrupted( )方法检查线程是否被中断,但是该调用同时会清除中断状态.
因此重复调用该方法不能保证得到相同的值.
4. 何时以及如何响应中断?
要中断某个线程,就调用这个线程的interrupt()方法.
调用interrupt()方法方法并不意味着立即停止目标线程正在进行的工作,而只是传递了请求中断的消息.
调用该方法后,中断只能在任务将要进入到阻塞操作中,或者任务已经在阻塞操作内部.(P701)
如果调用interrupt()方法时,线程没有阻塞则无能为力(不能使线程中断).
如下面的示例代码:
public class TestInterrupt { public static void main(String[] args) { Thread nonInterruptedThread = new Thread(new Runnable() { //这个run无法正常退出了,因为是无限循环 public void run() { while(true) { System.out.println("哈哈哈,你不能中断我."); } } }); nonInterruptedThread.start(); //这里调用interrupt无能为力,因为Runnable的run方法中没有任何阻塞发生的可能. //所以线程需要用别的方法退出(就是书中所说的第二种方式退出P701). nonInterruptedThread.interrupt(); } }
上面代码的改善:在run()方法中检查当前线程的中断状态
public class TestInterrupt { public static void main(String[] args) { Thread nonInterruptedThread = new Thread(new Runnable() { public void run() { //这里检查了当前线程的中断状态,只有在没有中断时执行 while(!Thread.currentThread().isInterrupted()) { System.out.println("好吧,你可以中断我."); } } }); nonInterruptedThread.start(); try { //让线程执行1秒之后中断. TimeUnit.SECONDS.sleep(1);. nonInterruptedThread.interrupt(); } catch (InterruptedException e) { System.out.println("sleep被中断"); } } }
可中断的阻塞,对中断的响应方式:提前返回,清除中断状态,抛出InterruptedException异常.
比如,Thread.sleep()或BlockingQueue.put().
注意,这里的清除中断状态,当代码中检测InterruptedException时,无法向上层抛出InterruptedException时,
应当在catch到这个异常中重新调用Thread.currentThread.interrupt();
所以,run()方法中通常的结构为:
public void run() { try() { while(!Thread.currentThread.isInterrupted()) { ... //假如这里有个操作在响应中断时,先清除中断状态,并InterruptedException抛出异常, //则Thread.currentThread.isInterrupted() 变成false了(因为中断状态在抛出异常中被清除了) } } catch(InterruptedException e) { //允许线程退出 Thread.currentThread.interrupted();//重新设置中断状态. } }
更合理的方式:
上面while(!Thread.currentThread.isInterrupted())循环中代码假如有个操作在响应中断时,
先清除中断状态,并抛出InterruptedException异常,则Thread.currentThread.isInterrupted()
变成false了(因为中断状态在抛出异常中被清除了),那么while循环中检测的条件就不再可靠了
(此时因为抛出异常而将程序执行点已转到catch(InterruptedException e)中,所以还是要使用检测
InterruptedException异常的方式更为可靠).
下面是一种合理方式,中断抛出的InterruptedException异常不能被向上传递时,恢复中断状态,
没有使用while(!Thread.currentThread.isInterrupted())
并使用一个变量保存中断信息,使得finally中总是可以恢复中断状态.(Java并发编程实战P118)
public Task getNextTask(BlockingQueue<gaskgt> queue) { boolean interrupted = false; try { return queue.take();//可能响应中断的某个操作, //在中断时将清除中断状态,并抛出InterruptedException异常 } catch(InterruptedException e) { interrupted = true;//为finally中处理保存中断状态 // } finally { if(interrupted) {Thread.currentThread.interrupt();}//恢复中断状态 } }
参考 Java 并发之线程中断 http://my.oschina.net/lifany/blog/163153
5. 处理不可中断阻塞
不可中断阻塞,要实现中断需要关闭当前正在阻塞的资源.
public class ReaderThread extends Thread { private final Socket socket; private final InputStream in; public ReaderThread(Socket socket) throws IOException { this.socket = socket; this.in = socket.getInputStream(); } public void interrupt() //覆盖interrupt方法,在中断时先关闭 不可中断的阻塞的资源 { try { socket.close(); } catch (IOException ignored) { } finally { super.interrupt(); } } public void run() { try { byte[] buf = new byte[BUFSZ]; while (true) { int count = in.read(buf); if (count < 0) break; else if (count > 0) processBuffer(buf, count); } } catch (IOException e) { /* Allow thread to exit */ } } }
赞 赏
微信赞赏
支付宝赞赏
本文固定链接: https://www.jack-yin.com/coding/thinking-in-java/2142.html | 边城网事