多线程之callable详解

面试有人会问:线程的实现方式有几种?
很多人可能回答:2种,继承Thread类,实现Runnable接口。
很多忽略了callable这种方式。
也许有人知道callable,也知道callable和Runnable的区别是callable可以有返回值,也可以抛出异常的特性,而Runnable没有。

这里估计很多人懵逼,接下来我们就从源码层次讲解这个问题。

注意callable可以有返回值,也可以抛出异常这点很关键。
很多时候我们让多线程去帮我们处理事情,是需要拿到返回值的,有了异常也可以处理,比如某宝的APP页面,一个页面展示3个块,而每个块展示的信息从后端获取的接口都不一样,那么是让前端调后端3次接口吗?
肯定不行,后端可以把3个块的信息,包装成一个接口,全部返回,那么问题来了,后端调用3个接口,比如第一个接口需要1秒,第二个需要2秒,第三个需要3秒,那么包装的这个接口响应时间最少6秒,怎么解决这个问题呢,可以用多线程来帮我们解决。
启动3个线程,每个线程去调用一个接口,那么3个线程一共执行完的时间就是最慢的那个线程的执行时间,这样接口的响应时间就变成了3秒,一下节省了一半的时间。
那么问题来了,线程如何把执行的业务代码的结果返回来呢?这时候就用到callable了。
 

    FutureTask futureTask=new FutureTask(new Callable() {
        @Override
        public String call() throws Exception {
            Thread.sleep(3000);
            System.out.println("calld方法执行了");
            return "call方法返回值";
        }
    });
    futureTask.run();
    System.out.println("获取返回值: " + futureTask.get());
    FutureTask futureTask1=new FutureTask(new Callable() {
        @Override
        public String call() throws Exception {
            Thread.sleep(3000);
            System.out.println("calld方法执行了1");
            return "call方法返回值1";
        }
    });
    futureTask1.run();
    System.out.println("获取返回值1: " + futureTask1.get());
 

我们可以调用futureTask.get()方法去获取线程异步的执行结果,
这时候问题又来了
那如果2个线程一起执行,futureTask执行完了,futureTask1没执行完会不会有问题。是不是futureTask1就拿不到结果了?
当然不会,futureTask.get()方法会保证线程在执行完之前是阻塞的。
我们看下futureTask源码

 2个一个有时间,一个没有时间,最终都是调用的awaitDone()这个方法,我们继续看awaitDone

 

 

线程没有执行完,也就是没有到达COMPLETING的状态,会执行LockSupport.park,让线程等待,那什么时候去唤醒呢?
我们继续看 

 

 

 

 最后返回线程的执行结果:

 

那么再问callable和Runnable有什么联系呢?
相信大家通过对源码的分析都明白了,callable的call()方法是被Runnable的run()方法调用的。

猜你喜欢

转载自blog.csdn.net/zhangjianming2018/article/details/126423361