在以前的开发过程中,经常会碰到这样一种场景:定义个接口,但是接口调用了异步的方法或者线程,导致这个接口没法直接返回结果,需要定义一个callback再将结果返回。
public int interface1(){
Util util = new Util();
util.call(new CallBack() {
@Override
public void onResult(int result) {
}
});
}
class Util{
void call(CallBack callBack){
if(callBack != null){
callBack.onResult(0);
}
}
}
private interface CallBack{
void onResult(int result);
}
如上所示,interface1调用了Util的call方法,想将result直接返回,但是由于Util的call 方法是异步的,导致 interface1 没法直接返回result。
解决办法:在 interface1里定义变量value,然后callback里赋值,最后返回。
public int interface1(){
final int[] value = {-1};
Util util = new Util();
util.call(new CallBack() {
@Override
public void onResult(int result) {
value[0] = result;
}
});
return value[0];
}
这样没有任何的问题。我当初以为上面的代码onResult回调可能会晚于return value[0];执行,其实不会,只要是在同一个线程里执行,即使callback里再嵌套N多个callback给value赋值。最终返回的value仍然是最后一个callback赋的值。(写代码凭感觉真不是好习惯!对待科学要严谨!)
在实际应用场景,util.call往往是一个异步方法,就是给call方法加了个子线程。
class Util{
void call(final CallBack callBack){
new Thread(){
@Override
public void run() {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(callBack != null){
callBack.onResult(0);
}
}
}.start();
}
}
如果上面的 interface1 不加控制,结果有可能就返回-1。原因是子线程还没有callback,
interface1 所在线程(这里我就称主线程了)就已经return了。
所以就引出了本篇文章的主题:同步接口返回异步调用结果。下面我总结几种解决办法,但基本的原理都是:
想办法让主线程等待,等子线程有结果了,主线程再返回。
目录
1.主线程里while循环监听到结果再返回
public int interface1(){
final int[] value = {-1};
Util util = new Util();
util.call(new CallBack() {
@Override
public void onResult(int result) {
value[0] = result;
}
});
while(value[0] == -1){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return value[0];
}
2.利用wait和notify
public int interface2(){
final int[] value = {-1};
final Object lock = new Object();
Util util = new Util();
util.call(new CallBack() {
@Override
public void onResult(int result) {
synchronized (lock){
value[0] = result;
lock.notify();
}
}
});
synchronized (lock){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return value[0];
}
3.利用 CountDownLatch或者CyclicBarrier
见https://www.jianshu.com/p/f00aa6f66281
这两种方式适用于多条件解锁的场景,就是主线程解除wait可能需要多个异步结果共同作用。
3.Future
适用于异步调用是可以自己掌控修改的场景,比如上面的Util.call里直接new Thread方式开启线程,那么只有通过callback的形式来返回结果。但是如果使用Future来开启线程,那接口Util.call完全可以改成同步的。
int call(){
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 0;
}
});
try {
return future.get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return -100;
}
0 条评论