在以前的开发过程中,经常会碰到这样一种场景:定义个接口,但是接口调用了异步的方法或者线程,导致这个接口没法直接返回结果,需要定义一个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 条评论

发表回复

您的电子邮箱地址不会被公开。