2009年2月12日星期四

控制线程 ---- 停止

写多线程的程序需要对并发带来的危险(竞争、死锁、脏读...)进行预防。同时还有一个重要的方面是对那些并行执行的代码进行控制,比如:停止、等待、继续。

让线程驱动的代码要么是被while括起来的需要长时间运行;要么是个临时的任务,运行到最后让线程自动终结。

为什么停止线程是个问题?因为线程的阻塞状态。
线程的四个状态:New, Runnable, Blocked, Dead
导致thread blocked的四个原因: sleep(sometime)、 wait()、I/O 操作、 synchronized导致的锁竞争。

需要停止线程操作的通常都是要终止长时间运行的线程。让线程停下来,大致有以下几种方法:
1)利用代码中while的条件判断语句,检测一(多)个是否需要停止的标志。
这个标志的访问需要同步控制。同时可以被其它线程代码改变。
2)使用interrupt()方法,让线程正在驱动的的代码产生InterruptedException
你可以在代码中捕获这个exception,进行适当的判断,然后让线程优雅的停止。
3)但是第二种方法对于因为以下两种原因造成的阻塞不起作用:IO和等待进入synchronized块。
针对IO造成的阻塞,可以关闭IO资源,这样迫使IO操作产生exception而让线程从阻塞状态返回,从而获得停止线程的机会。
4)因为等待锁而阻塞的线程利用传统的thread的api似乎没有什么好办法。一个建议是不要长时间的占用那些很多代码都想获得的锁
5)在提倡使用concurrent包的情况下。可以利用Executor.shutdownNow()停止所有其管理的线程。利用Executor.submit()获得的Future cancel(true)来对单独的线程进行停止操作。

**
现象:
启动一个线程执行一串代码,其中某步抛出了Error,注意是error不是Exception。这意味着你的线程嘎然而止。这种退出方式实在是太暴力了。:)
实践:
1)需要在你的线程执行的代码中捕获任何异常、错误。
Throwable 是Error、Exception的父类。而Exception又分为Runtime的exception和不是runtime的。我的实践是在run()的代码中catch (Throwable t),这样就可以保证你不遗漏任何异常了。
2)对产生错误的原因进行分析。具体问题具体分析。可以或应该继续执行的,请记录异常信息,继续代码的执行。如果需要退出线程的,需要注意释放在线程执行中申请的资源以及修改程序状态标志(某些应用数据)。