之前写了一篇关于FutureTask的Blog: http://rainbow702.iteye.com/admin/blogs/2206301
里面的源码如下(不包含之前写好的main方法):
public class Preloader {
private final FutureTask<Long> future = new FutureTask<Long>(new Callable<Long>() {
@Override
public Long call() throws Exception {
Thread.currentThread().setName("Thread(3)");
System.out.println(Thread.currentThread().getName() + ": simulate a latency ");
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": the latency is over");
return Math.round(Math.random() * 1000);
}
});
private final Thread loader = new Thread(future);
public void start() {
System.out.println(Thread.currentThread().getName() + ": start the loader");
loader.start();
System.out.println(Thread.currentThread().getName() + ": loader started");
}
public Long get() {
try {
System.out.println(Thread.currentThread().getName() + ": begin to get");
long start = System.currentTimeMillis();
Long result = future.get();
System.out.println(Thread.currentThread().getName() + ": got result: " + result);
System.out.println(Thread.currentThread().getName() + ": spent time: "
+ (System.currentTimeMillis() - start));
return result;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": got nothing");
return null;
}
}
当时写好的JUnit测试代码如下:
public class PreloaderTest {
/**
* Test method for {@link com.rainbow.util.futuretask.Preloader#get()}.
*/
@Test
public void testGet() {
Thread.currentThread().setName("Thread(main)");
final Preloader pl = new Preloader();
pl.start();
// try to get the result before the latency is over
new Thread(new Runnable() {
@Override
public void run() {
Thread.currentThread().setName("Thread(1)");
pl.get();
}
}).start();
// try to get the result after the latency is over
new Thread(new Runnable() {
@Override
public void run() {
Thread.currentThread().setName("Thread(2)");
try {
Thread.sleep(6000);
pl.get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
可是这段测试代码,不管怎么运行,运行多少次,它的运行结果却怎么也不是想像中的结果,却是如下的结果:
Thread(main): start the loader Thread(main): loader started Thread(3): simulate a latency Thread(1): begin to get
从结果看来,Thread(1) Thread(2) Thread(3) 都没有执行完了就被结束掉了。
对这个结果很是意外,于是上网查了一下,找到如下的一段说明:
在多线程环境下,程序退出的条件是,所有的非Daemon线程都正常结束或者某个线程条用了system.exit方法,导致进程强行退出。在eclipse下运行Junit的类是org.eclipse.jdt.internal.junit.runner.RemoteTestRunner。通过查看这个类的main方法。如下: public static void main(String [] args) { try { RemoteTestRunner testRunServer= new RemoteTestRunner(); testRunServer.init(args); testRunServer.run(); } catch (Throwable e) { e.printStackTrace(); // don't allow System.exit(0) to swallow exceptions } finally { // fix for 14434 System.exit(0); } } 显然,只要主线程结束,整个程序将会退出,这就是采用junit的时候奇怪退出程序的原因。
看到这段,也就明白了上面的结果了。
为了避免上面的这种情况,我想出来的两种方法:
① 不使用JUnit来测试,而是自己写个main方法来进行测试,就如http://rainbow702.iteye.com/admin/blogs/2206301 中那样
② 还使用JUnit,但在测试代码的最后加上sleep语句,即如下代码的最后的 sleep那样:
/**
*
*/
package com.rainbow.util.futuretask;
import org.junit.Test;
/**
* @author Rainbow
*/
public class PreloaderTest {
/**
* Test method for {@link com.rainbow.util.futuretask.Preloader#get()}.
*/
@Test
public void testGet() {
Thread.currentThread().setName("Thread(main)");
final Preloader pl = new Preloader();
pl.start();
// try to get the result before the latency is over
new Thread(new Runnable() {
@Override
public void run() {
Thread.currentThread().setName("Thread(1)");
pl.get();
}
}).start();
// try to get the result after the latency is over
new Thread(new Runnable() {
@Override
public void run() {
Thread.currentThread().setName("Thread(2)");
try {
Thread.sleep(6000);
pl.get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
但是这样的话,必须要正确估算出需要进行sleep的时间。 或者干脆使用不指定时间的 sleep() 。