Java面试题——Callable接口

一、Callable接口是创建多线程的方式之一

创建多线程的四种方式:
一、继承Thread类;
二、实现Runnable接口;
三、实现Callable接口;
四、使用线程池。

二、Callable接口与Runnable接口的区别

首先来看代码:

class MyThread implements Runnable{
    @Override
    public void run() {

    }
}
class MyThread2 implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        return null;
    }
}

再看Java8中Callable和Runnable的API:
在这里插入图片描述在这里插入图片描述
我们可以得出以下一些结论:
(1)Runnable接口没有返回值,Callable接口有返回值,泛型的数据类型是它的返回值类型。(在Demo中我定义的泛型为Integer,所以返回值类型为Integer)
(2)Runnable接口不会抛异常,Callable接口会抛异常。
(3)接口实现的方法不一样。Runnable实现的是run()方法,Callable实现call()方法。

三、FutureTask实现类

我们遍历Thread的构造方法, 发现没有一个是传Callable接口的。
我们发现RunnableFuture接口,它实现了Runnable接口,在它的实现类FutureTask中的构造方法里面有Callable< T >,可以整合Runnable和Callable< T >接口。

class MyThread implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        System.out.println("*********come in Callable");
        return 1024;
    }
}
public class CallableDemo {
    public static void main(String[] args) {
        //FutureTask(Callable<V> callable)
        FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());
        Thread t1 = new Thread(futureTask,"AA");
        t1.start();
    }
}

引申问题:那么既然有了Runnable接口,为什么还要Callable接口,谈一谈它的背景。

简单的说:并发、异步导致了Callable的诞生。
第一点:面向接口编程,适配器模式。
第二点:以前的方法,是串行的,只有main()一个线程。现在的方法,可以在执行的时候在某一个方法上的执行时间过长,例如有四个方法m1,m2,m3,m4执行,分别时间为1s,2s,10s和4s,那么在执行m3的时候,可能会发生阻塞,就可以创建一个新的线程AA进行执行,最后将结果进行汇总。

四、获取futureTask的计算结果

第一种方式:在最后使用futureTask.get();方法,如下:

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

/**
 * @Auther: Huntermax Lc
 * @Date: 2020/1/28
 * @Description: call
 * @version: 1.0
 */
class MyThread implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        System.out.println("*********come in Callable");
        //暂停一会线程
        try{
            TimeUnit.SECONDS.sleep(2);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        return 1024;
    }
}
public class CallableDemo {
    public static void main(String[] args) throws Exception{
        //两个线程,一个main主线程,一个AA futureTask
        //FutureTask(Callable<V> callable)
        FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());
        new Thread(futureTask,"AA").start();
        //int result02 =futureTask.get();

        System.out.println(Thread.currentThread().getName()+"*****");
        int result01 =100;
        int result02 =futureTask.get();//一般放在最后来取,可以自己测试前后的差别

        System.out.println("***result:"+(result01+result02));
    }
}

第二种:通过加入一个类似于自旋锁的判断。

while(!futureTask.isDone()){
            
        }

多个线程获取同一个futureTask线程计算的结果会复用,不会再重新计算。和线程池相关。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值