一、前言
我写文章总习惯开头写个前言,主要是用来介绍我遇到的问题,而所对应的文章,就是我解决这个问题的方法。
本文题目所说的代码同步是相对于代码异步的。做过联网或者其他耗时应用的开发者应该知道,执行耗时操作往往都是异步处理的, 不然会阻塞主线程。但是利用异步处理也带来了一个问题,因为代码执行是按照顺序的,如果你开个子线程执行耗时操作,原来代码中,它开完子线程就会继续下面的操作,不会等待子线程结果的,这样有时候就是下面的代码要用到子线程返回的值,这时候就为空(因为子线程值还没有返回),举个例子
//伪代码
int a = 1;
int b = 子线程返回结果;//这里开个子线程获取b的值
int c = a + b;//这时候就会出问题,因为执行这一步的时候b值还没有返回
按照上面伪代码例子,执行到 int c = a + b; 就会报错,原因是b的值为空,因为子线程还没结束。
当然也可以利用接口进行回调,然后在回调当中继续执行主线程中的代码,但是有时候这样的方法是麻烦的,回调多的时候处理起来也麻烦,我们这里也不是要讨论这个问题,毕竟很多时候一个问题的解决方法有很多种,当我们能够掌握多种之后,就能够选择最优的那种了,如果对回调方法感兴趣也可以看下我写过的另外一篇文章(android应用开发MVC框架(一)),里面就是有涉及到利用回调机制实现的。而本文就是要实现另外一种解决方式,就是使用CountDownLatch让代码实现同步,如果对CountDownLatch不是很了解务必度娘了解下。
二、demo描述问题
还是喜欢用实践的方式去描述和解决一个具体存在的问题,这样应该最有说服力。首先看看原先代码,这是没有使用CountDownLatch的。
程序入口,很简单:
public class TestActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
System.out.println("begin...");
Business business = new Business();
String s = business.getResult();//开启子线程
System.out.println("结果:"+s);
}
}
业务类,模拟耗时操作,这里没有使用CountDownLatch:
public class Business {
private String result = "";
public Business() {
}
public String getResult(){
new Thread(){
@Override
public void run() {
System.out.println("线程开始");
try {
Thread.sleep(5 * 1000);//测试超过5秒也不会ANR
result = "这是数据这是数据";
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("线程完成");
System.out.println("正确返回的结果应该是这样的:"+result);
}
}
}.start();
return result;
}
}
运行后在打印台可以看到以下结果,很清楚,主线程中先打印结果,结果为空,显然不是我们所要的
三、解决方式
现在对代码做如下修改,增加代码已注释,在业务处理中加入CountDownLatch
public class Business {
private String result = "";
public Business() {
}
public String getResult(){
final CountDownLatch countDownLatch = new CountDownLatch(1);//新增加的
new Thread(){
@Override
public void run() {
System.out.println("线程开始");
try {
Thread.sleep(15 * 1000);
result = "这是数据这是数据";
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("线程完成");
countDownLatch.countDown();//新增加的
System.out.println("正确返回的结果应该是这样的:"+result);
}
}
}.start();
try {
countDownLatch.await();//新增加的
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}
}
现在再次运行,可以发现打印的结果是在线程完成之后才打印的,结果也跟我们要的是一样的。这样就实现了代码的同步。