使用线程异常处理器提升系统可靠性
2015-11-17 14:19
267 查看
我们要编写一个Socket应用,监听指定端口,实现数据包的接收和发送逻辑,这在早期系统间进行数据交互是经常使用的,这类接口通常需要考虑两个问题:一是避免线程阻塞, 保证接收的数据尽快处理;二是接口的稳定性和可靠性问题,数据包很复杂,接口服务的系统也很多,一旦守候线程出现异常就会导致Socket停止响应,这是非常危险的,那我们有什 么办法来避免吗?
Java 1.5版本以后在Thread类中增加了 setUncaughtExceptionHandler方法,实现了线程 异常的捕捉和处理。可能大家会有一个疑问:如果Socket应用出现了不可预测的异常是否 可以自动重启呢?其实使用线程异常处理器很容易解决,我们来看一个异常处理器应用的例子,代码如下:
这段代码的逻辑比较简单,在TcpServer类创建时即启动一个线程,提供TCP服务, 例如接收和发送文件,具体逻辑在run方法中实现。同时,设置了该线程出现运行期异常 (也就是 Uncaught Exception )时,由 TcpServerExceptionHandler 异常处理器来处理。那 TcpServerExceptionHandler异常处理器做什么事呢?两件事:
□记录异常信息,以便査找问题。
□重新启动一个新线程,提供不间断的服务。
有了这两点,TcpServer就可以稳定地运行了,即使出现异常也能自动重启。客户端代 码比较简单,只需要new TcpServer()即可,运行结果如下:
系统正常运行:0 系统正常运行:1系统正常运行:2
线程Thread:0出现异常,自行重启,请分析原因。
java.lang.RuntimeException
at TcpServer.run{Client.java:30)
at java.lang.Thread.run(Thread.j ava:619)
系统正常运行:0 系统正常运行:1 系统正常运行:2
线程Thread-1出现异常,自行重启,请分析原因。
j ava.lang.RuntimeException
at TcpServer.run(Client.java:30)
atjava.lang.Thread.run(Thread.java:619)
从运行结果上也可以看出,当Thread-0出现异常时,系统自动启动了 Thread-1线程, 继续提供服务,大大提高了系统的可靠性。
这段程序只是一个示例程序,若要在实际环境中应用,则需要注意以下三个方面:
(1) 共享资源锁定
如果线程异常产生的原因是资源被锁定,自动重启应用只会增加系统的负担,无法提供不间断服务。例如一个即时通信服务器(XMPP Server)出现信息不能写入的情况时,即使再怎 么重启服务,也是无法解决问题的。在此情况下最好的办法是停止所有的线程,释放资源。
(2) 脏数据引起系统逻辑混乱
异常的产生中断了正在执行的业务逻辑,特别是如果正在执行一个原子操作(像即时 通信服务器的用户验证和签到这两个事件应该在一个操作中处理,不允许出现验证成功但签到不成功的情况),但如果此时抛出了运行期异常就有可能会破坏正常的业务逻辑,例如出 现用户认证通过了,但签到不成功的情况,在这种情景下重启应用服务器,虽然可以提供服
务,但对部分用户则产生了逻辑异常。
(3)内存溢出
线程异常了,但由该线程创建的对象并不会马上回收,如果再重新启动新线程,再创建一批新对象,特别是加入了场景接管,就非常危险了,例如即时通信服务,重新启动一个 新线程必须保证原在线用户的透明性,即用户不会察觉服务重启,在此种情况下,就需要在线程初始化时加载大量对象以保证用户的状态信息,但是如果线程反复重启,很可能会引起 OutOfMemory内存泄露问题。
Java 1.5版本以后在Thread类中增加了 setUncaughtExceptionHandler方法,实现了线程 异常的捕捉和处理。可能大家会有一个疑问:如果Socket应用出现了不可预测的异常是否 可以自动重启呢?其实使用线程异常处理器很容易解决,我们来看一个异常处理器应用的例子,代码如下:
class TcpServer implements Runnable{ @Override public void run() { // TODO Auto-generated method stub //正常业务运行,运行3秒 for (int i = 0; i < 3; i++) { try { Thread.sleep(1000); System.out.println("系统正常运行:"+i); } catch (Exception e) { // TODO: handle exception } } //抛出异常 throw new RuntimeException(); } public TcpServer() { // TODO Auto-generated constructor stub Thread t=new Thread(this); t.setUncaughtExceptionHandler(new TcpServerException()); t.start(); } } //异常处理器 class TcpServerException implements Thread.UncaughtExceptionHandler{ @Override public void uncaughtException(Thread t, Throwable e) { // TODO Auto-generated method stub //记录线程异常信息 System.out.println("线程:"+t.getName()+"出现异常"); e.printStackTrace(); //重启线程,保证业务不中断 new TcpServer(); } }
这段代码的逻辑比较简单,在TcpServer类创建时即启动一个线程,提供TCP服务, 例如接收和发送文件,具体逻辑在run方法中实现。同时,设置了该线程出现运行期异常 (也就是 Uncaught Exception )时,由 TcpServerExceptionHandler 异常处理器来处理。那 TcpServerExceptionHandler异常处理器做什么事呢?两件事:
□记录异常信息,以便査找问题。
□重新启动一个新线程,提供不间断的服务。
有了这两点,TcpServer就可以稳定地运行了,即使出现异常也能自动重启。客户端代 码比较简单,只需要new TcpServer()即可,运行结果如下:
系统正常运行:0 系统正常运行:1系统正常运行:2
线程Thread:0出现异常,自行重启,请分析原因。
java.lang.RuntimeException
at TcpServer.run{Client.java:30)
at java.lang.Thread.run(Thread.j ava:619)
系统正常运行:0 系统正常运行:1 系统正常运行:2
线程Thread-1出现异常,自行重启,请分析原因。
j ava.lang.RuntimeException
at TcpServer.run(Client.java:30)
atjava.lang.Thread.run(Thread.java:619)
从运行结果上也可以看出,当Thread-0出现异常时,系统自动启动了 Thread-1线程, 继续提供服务,大大提高了系统的可靠性。
这段程序只是一个示例程序,若要在实际环境中应用,则需要注意以下三个方面:
(1) 共享资源锁定
如果线程异常产生的原因是资源被锁定,自动重启应用只会增加系统的负担,无法提供不间断服务。例如一个即时通信服务器(XMPP Server)出现信息不能写入的情况时,即使再怎 么重启服务,也是无法解决问题的。在此情况下最好的办法是停止所有的线程,释放资源。
(2) 脏数据引起系统逻辑混乱
异常的产生中断了正在执行的业务逻辑,特别是如果正在执行一个原子操作(像即时 通信服务器的用户验证和签到这两个事件应该在一个操作中处理,不允许出现验证成功但签到不成功的情况),但如果此时抛出了运行期异常就有可能会破坏正常的业务逻辑,例如出 现用户认证通过了,但签到不成功的情况,在这种情景下重启应用服务器,虽然可以提供服
务,但对部分用户则产生了逻辑异常。
(3)内存溢出
线程异常了,但由该线程创建的对象并不会马上回收,如果再重新启动新线程,再创建一批新对象,特别是加入了场景接管,就非常危险了,例如即时通信服务,重新启动一个 新线程必须保证原在线用户的透明性,即用户不会察觉服务重启,在此种情况下,就需要在线程初始化时加载大量对象以保证用户的状态信息,但是如果线程反复重启,很可能会引起 OutOfMemory内存泄露问题。
相关文章推荐
- 沉浸式菜单栏,标题栏与状态栏的的距离
- cf+在于优化去除一些重复耗时的操作+明白了runtime意义了
- 阿里云服务器菜鸟使用(一)
- 18,位计算
- storm学习-基本概念及入门示例
- 在win7下使用git和gitlab进行code review
- 如何使用Live CD来修复Grub / Grub2
- mysql水平分表和垂直分表和数据库分区
- sed 命令详细讲解
- 杭电2700 Parity
- 关于angularjs的指令中的scope
- 如何使用Live CD来修复Grub / Grub2
- RabbitMQ基础概念详细介绍
- UNICODE转码成中文
- javascript中,new操作符的工作原理是什么?
- iOS自定义的表情键盘
- 【C++】迷宫
- 02 第一个Netty程序
- uses-permission android:name="android.permission.INSTALL_PACKAGES 报错
- float转int 四舍五入问题