专栏名称: 西界__
今天看啥  ›  专栏  ›  西界__

JUC之Callable接口

西界__  · 简书  ·  · 2021-01-04 08:24

文章预览

概述

通过对 多线程 的学习,我们知道创建线程有三种方式,分别是继承Thread类,实现Runnable接口,实现Callable接口。我们目前主要使用的都是使用实现Runnable接口创建线程,但是还是需要掌握使用Callable来创建线程。

由两者的代码比较可知,使用Runnable创建线程运行是没有返回值的,而Callable是由返回值的。我们查看两者的文档

发现Callable是可以运行抛出异常的,而Runnable是不返回结果,也不能抛出被检查的异常。

直接继承Thread,另外一种就是实现Runnable接口。这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。

使用

实现了Runnable接口之后如何使用呢?照样查看Java官方文档

查看了Thread的所有构造方法,并没有发现与Callable相关的方法。那我们怎么使用呢?

一般情况下是配合 ExecutorService 来使用的,也就是前面讲到的。这里我们讲的是另一种方式。首先查看Runnable文档

我们发现Runnable有一个子接口 RunnableFuture ,既然RunnableFuture是Runnable的子接口所有我们可以传该接口,查看文档,

我们发现了RunnableFuture有一个实现类 FutureTask ,点击查看文档

查看构造方法~找到了一个可以传入Callable的构造方法!!

也就是说我们可以通过 FutureTask 接口来运行Callable。

public class ThreadDemo {
    public static void main(String[] args) {

        FutureTask<Integer> futureTask = new FutureTask<>(new Data());

        new Thread(futureTask).start();
    }
}


class Data implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        System.out.println("Callable创建线程");
        return 1024;
    }
}

这里可以看见线程已经创建并启动成功了!但是如何获取到call方法到返回值呢??我们可以通过 get() 方法获取

public class ThreadDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        FutureTask<Integer> futureTask = new FutureTask<>(new Data());

        new Thread(futureTask).start();
        //获取call方法的返回值
        System.out.println(futureTask.get());
    }
}


class Data implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        System.out.println("Callable创建线程");
        return 1024;
    }
}

细节

当我们使用 FutureTask.get() 获取到返回结果时,如果线程执行还没有结束,是会导致其他线程被阻塞的,因为我们需要一直等待获取的结果。(也有重载方法设置等待时间)

public class CallableDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //两个线程,一个main主线程,一个是AA-futureTask线程

        //public FutureTask(Callable<V> callable)
        //创建一个 FutureTask ,它将在运行时执行给定的 Callable 。
        FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());
        new Thread(futureTask,"AA").start();

        //建议放在后面写
        Integer result = futureTask.get();//要求获得Callable线程的计算结果,如果没有计算完成就要去强求,会导致堵塞,直到计算完成
        System.out.println(Thread.currentThread().getName());

        System.out.println("result:"+result);

    }
}

class MyThread implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"\tCome in Callable");
        TimeUnit.SECONDS.sleep(3);
        return 1024;
    }
}

所以我们通常被get()放到最后面。

一个任务不能被反复执行,如果想反复执行,必须重新设置新任务。

设置新任务~

public class CallableDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //两个线程,一个main主线程,一个是AA-futureTask线程

        //public FutureTask(Callable<V> callable)
        //创建一个 FutureTask ,它将在运行时执行给定的 Callable 。
        FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());
        FutureTask<Integer> futureTask2 = new FutureTask<>(new MyThread());

        new Thread(futureTask,"AA").start();
        new Thread(futureTask2,"BB").start();

        //建议放在后面写
        Integer result = futureTask.get();//要求获得Callable线程的计算结果,如果没有计算完成就要去强求,会导致堵塞,直到计算完成
        System.out.println(Thread.currentThread().getName());

        System.out.println("result:"+result);

    }
}

class MyThread implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"\tCome in Callable");
        TimeUnit.SECONDS.sleep(3);
        return 1024;
    }
}
………………………………

原文地址:访问原文地址
快照地址: 访问文章快照
总结与预览地址:访问总结与预览