应用程序不能全然结束的原因探秘及调试方法

把程序部署到Tomcat或WebLogic后,常常碰到结束程序时不能全然结束,某些线程还活着,必须手动强制关闭整个server才干够结束.但假设我们的应用server上部署了非常多个重要应用,当中一个应用的启停应该不能影响其它应用才对.到底是什么原因导致了我们的server不能关闭呢?预计非常多人没有深入研究过这个问题吧.

先看看以下的样例吧:

package com.tgb.lk.thread;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadDemo {
	private Timer timer;
	private ExecutorService threadPool;
	private Thread thread1;
	private Thread thread2;
	private Thread thread3;

	public static void main(String[] args) {
		ThreadDemo threadDemo = new ThreadDemo();
		threadDemo.init();
		try {
			Thread.currentThread().join();
		} catch (InterruptedException e) {
		}

	}

	public void init() {
		// 1.启动Timer
		timer = new Timer();
		timer.schedule(new TimerTask() {
			@Override
			public void run() {
				System.out.println(System.currentTimeMillis());
			}
		}, 0, 3000);

		// 2.启动线程池
		threadPool = Executors.newFixedThreadPool(3);
		for (int i = 0; i < 10; i++) {
			final int inner = i;
			threadPool.execute(new Runnable() {
				@Override
				public void run() {
					System.out.println(inner);
				}
			});
		}

		// 3.线程挂起
		thread1 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					Thread.sleep(1000000000);// 假设时间设置的超长,看上去也相当于挂起了线程.
				} catch (InterruptedException e) {
				}
			}
		});
		thread1.start();

		// 4.线程挂起2
		thread2 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					Thread.currentThread().join();
					Thread.currentThread().wait();
				} catch (InterruptedException e) {
				}
			}
		});
		thread2.start();

		// 5.线程挂起3
		thread3 = new Thread(new Runnable() {
			@Override
			public void run() {
				Thread.currentThread().suspend();// 挂起当前线程,可能会形成死锁哦,所以不建议使用suspend
			}
		});
		thread3.start();

	}

}

运行后我们会发现main方法不能自己主动结束,原因当然是init方法中的一些线程堵塞了程序结束.怎样验证我的想法呢?

使用JConsole能够查看,假设你还没听过这个工具就有点out了,JDK5+都自带这个工具.

当我们凝视掉代码中main方法的Thread.currentThread().join();后再执行看Jconsole线程,结果例如以下:

原来Timer, 线程池, 线程操作中的sleep,wait,suspend等方法都能阻止我们的线程结束啊,找到原因了就找解决的方法吧,看以下代码:

package com.tgb.lk.thread;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadDemo {
	private Timer timer;
	private ExecutorService threadPool;
	private Thread thread1;
	private Thread thread2;
	private Thread thread3;

	public static void main(String[] args) {
		ThreadDemo threadDemo = new ThreadDemo();
		threadDemo.init();
//		try {
//			Thread.currentThread().join();
//		} catch (InterruptedException e) {
//		}
		threadDemo.destory();
	}

	public void init() {
		// 1.启动Timer
		timer = new Timer();
		timer.schedule(new TimerTask() {
			@Override
			public void run() {
				System.out.println(System.currentTimeMillis());
			}
		}, 0, 3000);

		// 2.启动线程池
		threadPool = Executors.newFixedThreadPool(3);
		for (int i = 0; i < 10; i++) {
			final int inner = i;
			threadPool.execute(new Runnable() {
				@Override
				public void run() {
					System.out.println(inner);
				}
			});
		}

		// 3.线程挂起
		thread1 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					Thread.sleep(1000000000);// 假设时间设置的超长,看上去也相当于挂起了线程.
				} catch (InterruptedException e) {
				}
			}
		});
		thread1.start();

		// 4.线程挂起2
		thread2 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					Thread.currentThread().join();
					Thread.currentThread().wait();
				} catch (InterruptedException e) {
				}
			}
		});
		thread2.start();

		// 5.线程挂起3
		thread3 = new Thread(new Runnable() {
			@Override
			public void run() {
				Thread.currentThread().suspend();// 挂起当前线程,可能会形成死锁哦,所以不建议使用suspend
			}
		});
		thread3.start();

	}

	public void destory() {
		if (timer != null) {
			timer.cancel();
		}

		if (threadPool != null) {
			threadPool.shutdown();
		}

		if (thread1 != null) {
			thread1.interrupt();
		}

		if (thread2 != null) {
			thread2.interrupt();
		}

		if (thread3 != null) {
			thread3.stop();
		}

	}
}

这段代码相比第一段代码我们在main中调用了destory这个方法.destory方法结束了timer,threadPool等对象的线程.执行一下代码,结果程序能够自己主动结束了,这时已经不再须要jconsole了.

拓展一下, 非常多程序都用spring来管理,假设在spring创建了线程,这些线程希望在spring结束时才去结束该怎么办呢?

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class ClazzDaoImpl implements InitializingBean, DisposableBean {
	@Override
	public void afterPropertiesSet() throws Exception {
		//这里写初始化的方法
		// init()
	}

	@Override
	public void destroy() throws Exception {
		//这里写结束线程的方法
		// destory()
	}
}

希望读者能从上面的过程中学到调试这样的问题的思路和方法,同一时候写代码时多多注意线程问题,自己的程序开启了对象尽量在自己的程序中同一时候写上结束对象的方法.

来个总结,非常多程序猿会用Timer,会用线程和线程池,但是非常少人关心这些对象的回收,假设这些线程不回收就会造成应用server结束不掉,有时还会造成内存溢出.有些人可能说我非常会写框架,但为什么流行度不广呢,由于咱们通常都考虑的太少,看看国外的优秀框架.是不是非常多类里面都有init和destory方法,这些框架大多数都考虑了对象的回收问题.作优秀的程序猿,从优秀的代码习惯開始吧~

时间: 2024-10-24 19:06:08

应用程序不能全然结束的原因探秘及调试方法的相关文章

应用程序不能完全结束的原因探秘及调试方法

把程序部署到Tomcat或WebLogic后,经常碰到结束程序时不能完全结束,某些线程还活着,必须手动强制关闭整个服务器才可以结束.但如果我们的应用服务器上部署了很多个重要应用,其中一个应用的启停应该不能影响其他应用才对.究竟是什么原因导致了我们的服务器不能关闭呢?估计很多人没有深入研究过这个问题吧. 先看看下面的例子吧: package com.tgb.lk.thread; import java.util.Timer; import java.util.TimerTask; import j

了解 Oracle Berkeley DB 可以为您的应用程序带来 NoSQL 优势的原因及方式。

将 Oracle Berkeley DB 用作 NoSQL 数据存储 作者:Shashank Tiwari 2011 年 2 月发布 “NoSQL”是在开发人员.架构师甚至技术经理中新流行的一个词汇.尽管这个术语最近很流行,但令人惊讶的是,它并没有一个普遍认可的定义. 通常来说,任何非 RDBMS 且遵循无模式结构的数据库一般都不能完全支持 ACID 事务,并且因高可用性的承诺以及在横向伸缩环境中支持大型数据集而普遍被归类为“NoSQL 数据存储”.鉴于这些共同特征(与传统的 RDBMS 的特征

转 : 终于搞清楚了为什么Java桌面程序总是感觉慢的原因

转自: http://my.oschina.net/u/2306127/blog/370495 终于搞清楚了为什么Java桌面程序总是感觉慢的原因!    按照道理服务器和浏览器都在用脚本和虚拟机,那些不慢,而Java的桌面程序总是感觉响应迟钝呢?主要有两个原因,一是服务器和浏览器都是异步的,提交到渲染出来这中间有足够的时间去处理,而且网络IO要远远慢于本地CPU调用:二是浏览器的界面其实不是JavaScript渲染(很多人可能都会以为HTML是JS绘制的),而是浏览器在操作系统层原生支持的,甚

嵌入式linux应用程序调试方法

嵌入式linux应用程序调试方法 四 内存工具 17 4.1 MEMWATCH 17 4.2 YAMD 22 4.3 Electric Fence 24 五 C/C++代码覆盖.性能profiling工具 24 5.1 用gcov来测试代码覆盖率 25 5.2 使用gprof来优化你的C/C++程序 35 四 内存工具 您肯定不想陷入类似在几千次调用之后发生分配溢出这样的情形. 许多小组花了许许多多时间来跟踪稀奇古怪的内存错误问题.应用程序在有的开发工作站上能运行,但在新的产品工作站上,这个应用

程序员生存定律--升华成高手的可能方法

程序员生存定律这系列的目录在这里:程序员生存定律--目录 喜欢从头瞄的,可以移步. ------------------------------------------------------------------------------ 一旦度过了初始阶段,做过了前面说的那些事情,那么一个人算是基本入行了,接下来的目标就非常简单,要在选定方向上成为高手.高手意味着专业,而在分工无限细化的年代里,专业则是生存.发展好最为重要的一个前提. 1 高手的定义和养成关键 我估计如果问100个人“什么样

linux Ubuntu(Segmentation fault)段错误出现原因及调试方法

  在linux下编译了一个程序,尝试运行的时候出现: Segmentation fault (core dumped) 初步确认为...完全不知道是什么玩意. 于是找度娘了. ---------------------------------------------------------------------------- 出现原因 原来这个东西叫做段错误,就程序运行的时候出现内存错误.有很多原因会导致这样的内存错误,但是应该把这些问题归结于程序的错误,那么程序是出现了什么样的错误了呢,为

【转】delphi程序只允许运行一个实例的三种方法:

一.        创建互斥对象 在工程project1.dpr中创建互斥对象 Program project1 Uses Windows,Form, FrmMain in 'FrmMain.pas' {MainForm}; {$R *.res} var hAppMutex: THandle; //声明互斥变量 begin hAppMutex := CreateMutex(nil, false,’projectname’); //创建互斥对象projectname工程名称 if ( (hAppM

[转] python程序的调试方法

qi09 原文 python程序的调试方法 本文讨论在没有方便的IDE工具可用的情况下,使用pdb调试python程序 源码例子 例如,有模拟税收计算的程序: #!/usr/bin/python def debug_demo(val): if val <= 1600 : print "level 1" print 0 elif val <= 3500 : print "level 2" print (val - 1600) * 0.05 elif val

Linux环境下段错误的产生原因及调试方法小结(转)

最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且 项目工程庞大复杂,出现了不少问题,其中遇到最多.花费时间最长的问题就是著名的“段错误”(Segmentation Fault).借此机会系统学习了一下,这里对Linux环境下的段错误做个小结,方便以后同类问题的排查与解决. 1. 段错误是什么 一句话来说,段错误是指访问的内存超出了系统给这个程序所设定的内存空间,例如访问了不存在的内存地址.访问了系统保护的内存地址.访问了只读的内存地址等等情况.这里贴一个对于“段