Android TestCase中Thread问题
最近在研究Android TestCase,由于业务中很多代码采用的线程异步和回调的方式实现,打算用TestCase来检测下,没有想到在TestCase的地方卡了不少时间,发现在执行TestCase的时候,如果方法中线程问题,那么则回调则不能执行。
public class ApplicationTest extends ApplicationTestCase<AppContext> {
AppContext appContext;
public ApplicationTest() {
super(AppContext.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
createApplication();
appContext = getApplication();
}
public void testThread() {
new Thread() {
@Override
public void run() {
assertTrue(1 == 2);
}
}.start();
}
}
如上代码,在执行的,代码并没有问题,这个很显然是有问题的。Google一大堆问题,发现是有问题的。
解决方法如下,就是添加一个CountDownLatch,解放方法如下:
public void testThread() throws InterruptedException {
final CountDownLatch signal = new CountDownLatch(1);
new Thread() {
@Override
public void run() {
assertTrue(1 == 2);
signal.countDown();
}
}.start();
signal.await();
}
这样再执行的时候,就会直接报红色,测试不能通过。
然后继续了百度下CountDownLatch的用法,总结如下:
java.util.concurrent
类 CountDownLatch
java.lang.Object
java.util.concurrent.CountDownLatch
一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。
下面举个例子来说明下CountDownLatch的用法,比如一个程序需要执行10任务,等到10个任务全部完成以后,再去释放某些资源。
public class TestCountDownLatch {
public static void main(String[] args) {
Executor executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
final int finalI = i;
executor.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("task %d success.%n", finalI);
}
});
}
System.out.println("main thread finish");
}
}
如上代码,直接结果如下
main thread finish
task 1 success.
task 4 success.
task 3 success.
task 2 success.
task 0 success.
task 6 success.
task 9 success.
task 7 success.
task 8 success.
task 5 success.
这个完全不是我们想要的,但是一般的话,我们可以用Java里面的Future来搞定。
public class TestCountDownLatch {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
List<Future> futures = new ArrayList<Future>();
for (int i = 0; i < 10; i++) {
final int finalI = i;
futures.add(executor.submit(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("task %d success.%n", finalI);
}
}));
}
int i = 0;
for (Future future : futures) {
try {
System.out.println("future finished " + i + "-->" + future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
System.out.println("main thread finish");
}
}
然后执行的结果是如下
task 1 success.
task 4 success.
task 3 success.
task 2 success.
task 0 success.
future finished 0–>null
future finished 0–>null
future finished 0–>null
future finished 0–>null
future finished 0–>null
task 6 success.
task 8 success.
task 9 success.
task 5 success.
task 7 success.
future finished 0–>null
future finished 0–>null
future finished 0–>null
future finished 0–>null
future finished 0–>null
main thread finish
从结果可以看出main thread finish是最用打印出来的,说明等到10个任务结束后,然后就可以释放某些资源了。
然后我们在用CountDownLatch来实现如上功能,发现代码简介不少。
public class TestCountDownLatch {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
final CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
final int finalI = i;
executor.submit(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("task %d success.%n", finalI);
countDownLatch.countDown();
}
});
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main thread finish");
}
}
task 1 success.
task 4 success.
task 3 success.
task 2 success.
task 0 success.
task 6 success.
task 9 success.
task 8 success.
task 7 success.
task 5 success.
main thread finish
执行的结果还是如我们预期那样.
从上面代码所看,其实就是把一执行的现场阻塞,然后等待所有的子线程执行完成后,再通知执行现场调用即可。
我们再来看个例子
public class TestCountDownLatch {
public static void main(String[] args) {
Thread thread = new Thread() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.printf("i = %d%n", i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread.start();
System.out.println("main thread finish");
}
}
结果如下所示
i = 0
main thread finish
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
现在我们不适用Future和CountDownLatch单纯使用Thread来搞定。
public class TestCountDownLatch {
public static void main(String[] args) {
final Object lock = new Object();
Thread thread = new Thread() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.printf("i = %d%n", i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (lock) {
lock.notify();
}
}
};
thread.start();
try {
synchronized (lock) {
lock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main thread finish");
}
}
可以直接使用wati和notify来解决。
或者也是可以这样。
public class TestCountDownLatch {
public static void main(String[] args) {
Thread thread = new Thread() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.printf("i = %d%n", i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main thread finish");
}
}
直接使用线程的join,可以搞定的,结果都是一样的。
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
main thread finish