从事多年C++开发后,一个偶然的机会接触到一个JAVA的项目,刚接手的时候除了语法其他都是按照C++的习惯来编码的,在编码的过程中,初始化的时候很自然的new出了一个对象,但是当我想删除这个对象的时候,又很自然的写出了delete xxx; 这下问题大了,好像JAVA里没有这个关键字?反正我编译错误了。后来想了想,JAVA可以很强大的把垃圾回收啊,没有必要像C++一样要手动删除的。从那以后一直没关注过这类问题,直到后来一次评审其他人的代码时,发现了下面的有趣的问题。
首先看一下如下的代码:
public void restart() {
if (null != mXXXThread) {
mXXXThread.cancel();
mXXXThread = null;
}
if (null == mXXXThread) {
mXXXThread = new XXXThread();
mXXXThread.start();
}
}
大概意思是说要重新启动一个线程,在停止该线程后,直接将该对象设置为null,然后重新创建一个对象,并启动这个线程。
这段代码没什么错误,也可以很好的运行,至少一段时间内没有在这里出过问题。
但是,创建一个线程并启动,这是一个耗费资源的过程,并且上述的代码如果直接将对象赋值为null,是否一定能保证那个线程就会销毁么?由于JAVA经验较少,并不能确定该代码的正确性,还请JAVA大神解答。
究竟为什么对这么小的问题耿耿于怀,一方面是不能自己释放一个东西感到不快,另一方面是公司的代码检查不允许直接将一个对象赋值为null。
大家都知道,创建一个线程是耗费资源的,理论都知道,但是写代码的时候就和这些理论说拜拜了。大部分的软件公司的员工都会写出这样的代码,放眼看去一定是没问题的,但是当你觉得软件慢了,性能不佳了,这些小地方都是问题。
最初想象使用同一个对象,仅生成一次,该线程停止后再调用一次start,于是有了如下的代码:
public void restart()
{
mXXXThread.cancel();
if (mXXXThread.isAlive()) {
mXXXThread.join();
}
mXXXThread.setparam(param);
mXXXThread.start();
}
很强迫症的要确定上一个线程停止后再重新设置参数并启动线程,不必重新生成一次对象,只要重新启动一次线程就好。
看到这里大神一定看出了一个很严重的错误:线程不可以重复start!!!!!!!
好吧,天真一次。如何重复利用一个对象呢?这次看到了线程池。
Executor提供了4种线程池,作为JAVA新手我不过多介绍,满足我的使用条件的仅有SingleThreadExecutor。
该线程池的好处就是单线程池,控制仅有一个线程在运行。
将原来的Thread的子类改成实现Runnable接口的类,一切都解决,我不再需要关心线程停止后再重新开启线程,甚至我可以一直使用一个Runnable对象而不需要重新创建,代码如下:
public xxxRunnable implements Runnable {
private boolean mStop = false;
public void setparam(int param)
{
//save param
}
public void cancel()
{
mStop = true
}
public void run()
{
mStop = false;
//do something with param
while (!mStop) {
//working
}
}
}
上述为Runnable的实现代码,不太善于使用stop的方法停止线程,所以使用标志位的方式停止线程。可以看到,设置参数后,在线程真正运行时才会使用这些参数。而restart代码如下:
public void restart()
{
mXXXRunnable.cancel();
mXXXRunnable.setparam();
mSingleExecutor.executor(mXXXRunnable);
}
这就能重新运行?
cancel之后线程不会停止吧,马上设置参数不会影响上一个线程运行么?这就要根据实际的业务逻辑需要了。这里我说不会影响,因为适合我的逻辑,因为传入的参数是一个socket,而我在过程中实际需要的是InputStream……在run中我会通过socket来获取它的inputstream,所以就算外界设置了其他的socket,也不会影响当前的run(),但是这不是重点,重点是线程池在上次的线程停止运行后执行新的线程,也就是重新设置了参数的runnable,好了,不用重新生成对象了,虽然有点冒险,好在避过检查了。
对于JAVA的对象管理,习惯了new出的对象必须要delete之后,就不太习惯JAVA,也许天生苦命,就是喜欢挑战内存泄漏。不过,对于线程,始终很感兴趣,因为很多的项目失败在多线程和多进程,类似于上述代码,如果稍微复杂的逻辑我想一定会出问题,如果多个线程使用同一份数据更会出问题,如何控制好线程的管理,不只是一个技术,更是一种艺术。
作为新手抛砖引玉,希望得到更多指导。