Callable接口详解

279 阅读2分钟

这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战

前言

之前介绍了创建线程的四种方式今天来仔细的讲一讲Callable接口

创建线程的四种方式

  1. 继承Thread
public class Thread_ {
    public static void main(String[] args) {
        Data1 data1 = new Data1(); // 创建对象
        data1.start(); // 开启线程
    }
}

/**
 * 继承Thread类,重写run方法
 */
class Data1 extends Thread{
    @Override
    public void run() {
        System.out.println("线程内方法");
    }
}
  1. 实现Runnable接口
public class Thread_ {
    public static void main(String[] args) {
        Data1 data1 = new Data1(); // 创建对象
        new Thread(data1).start(); // 将对象放入构造方法中,并开启线程
    }
}
/**
 * 实现Runnable接口,重写run方法
 */
class Data1 implements Runnable{
    @Override
    public void run() {
        System.out.println("线程内方法");
    }
}
  1. 实现Callable接口
public class Thread_ {
    public static void main(String[] args) {
        Data1 data1 = new Data1(); // 创建对象
        FutureTask<String> futureTask = new FutureTask<String>(data1); // 创建 futureTask对象将实现Callable的类的对象放到构造方法中
        new Thread(futureTask).start(); // 将对象放入构造方法中,并开启线程
    }
}

/**
 * 实现Callable接口,重写run方法
 */
class Data1 implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("线程内方法");
        return "11";
    }
}
4. 使用线程池
public class Test1 {
    public static void main(String[] args) {
        // 单个线程
        ExecutorService threadPool = Executors.newSingleThreadExecutor(); // 单个线程
        try{
            for (int i = 1; i <= 5; i++) {
                // 使用线程池,来创建线程
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "ok");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

Callable的使用

可以看出除了使用线程池方法外我们都需要通过手动使用Thread类来开启线程

那么我们来看一下Thread类的构造方法

image-20211113211733333

可以看到我们需要传入Runnable接口实现类或者不传参

那么Callable接口和Runnable接口并无直接的关联,要怎么使用呢?

解决方法就在下面的类图中

image-20211113212746350

我们可以看到 RunnableFuture接口继承了Runnable接口,而它有一个实现类为FutureTask,其构造方法可以传入Callable接口,这样Runnable接口就和Callable接口就存在了关系

Callable接口的优点

class MyThread implements Callable<String>{
    // 泛型为返回的数据的类型
    @Override
    public String call() throws Exception {
        return "rer";
    }
}

从上面Callable接口的自定义实现类来看,Callable的接口相比较Runnable接口存在以下几点优点

  1. 可以有返回值

  2. 可以抛出异常

还有一些细节是要注意的

  1. FutureTask 会判断线程状态,所以只会被线程run一次
  2. 因为要先执行线程里的方法,所以获取返回值可能会被阻塞