首页 > 代码库 > 应用程序不能全然结束的原因探秘及调试方法

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

把程序部署到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方法,这些框架大多数都考虑了对象的回收问题.作优秀的程序猿,从优秀的代码习惯開始吧~


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