Future、Callable和FutureTask

我们知道创建一个线程有两种方式,一种是实现Thread类,一种是实现Runnable接口,这两种方法都有一个缺点,就是没法拿到线程的执行结果,一个变通的方法,就是使用共享变量,间接的返回线程的执行结果。在JDK1.5之后,java.util.concurrent包中提供了两个新的接口:Future和Callable。

1、Callable接口

先看Callable接口,Callable类似于Runnable接口,通过实现Callable接口,也可以自定义一个线程。Callable接口一般和ExecutorService配合使用,ExecutorService有一个submit方法,submit方法有三个重载版本:

 Future submit(Callable task);
 Future submit(Runnable task, T result);
Future submit(Runnable task);

2、Future接口

从上面我们知道ExecutorService 的submit方法的返回值是Future接口,Future接口如下所示:

public interface Future {
   boolean cancel(boolean mayInterruptIfRunning);
   boolean isCancelled();
   boolean isDone();
   V get() throws InterruptedException, ExecutionException;
   V get(long timeout, TimeUnit unit)
       throws InterruptedException, ExecutionException, TimeoutException;
}

从Future接口提供的方法,可以知道Future接口具有如下功能:取消Callable任务的执行,查看Callable任务是否已经取消、是否已经完成,获取Callable任务的执行结果。这里get()的是阻塞方法,调用get()方法之后,直到Callable任务返回执行结果,否则,线程会一直阻塞在这里。因为Future是一个接口,所以是无法直接使用的,因此就有了下面的FutureTask。

3、FutureTask接口

public class FutureTask implements RunnableFuture
public interface RunnableFuture extends Runnable, Future

FutureTask实现了RunnableFuture接口,而RunnableFuture接口继承了Runnable和Future接口,所以可以FutureTask是Runnable和Future接口的子类,既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

代码示例1

package com.async.future;

import java.util.concurrent.*;

/**
 * Created by linbo on 2018/4/30.
 */
public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        Task task = new Task();
        Future future = executorService.submit(task);
        executorService.shutdown();
        System.out.println("main thread is running");
        System.out.println("Callable return value = " + future.get());
        System.out.println("main thread finished");
    }

    static class Task implements Callable {
        @Override
        public String call() throws Exception {
            Thread.sleep(1000);
            System.out.println("Callable thread is running");
            return "hello world";
        }
    }
}

执行结果:

main thread is running
Callable thread is running
Callable return value = hello world
main thread finished

代码示例2

package com.async.future;

import java.util.concurrent.*;

/**
 * Created by linbo on 2018/4/30.
 */
public class FutureTaskTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        Task task = new Task();
        FutureTask futureTask = new FutureTask(task);
        executorService.submit(futureTask);
        executorService.shutdown();
        System.out.println("main thread is running");
        System.out.println("Callable return value = " + futureTask.get());
        System.out.println("main thread finished");
    }

    static class Task implements Callable {
        @Override
        public String call() throws Exception {
            Thread.sleep(1000);
            System.out.println("Callable thread is running");
            return "hello world";
        }
    }
}

执行结果:

main thread is running
Callable thread is running
Callable return value = hello world
main thread finished

其实在代码示例1中,future引用变量指向的实例就是一个FutureTask实例,这两段代码在本质上没有区别。

代码示例3

package com.async.future;

import java.util.concurrent.*;

/**
 * Created by linbo on 2018/5/1.
 */
public class TwoCallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        long startTime = System.currentTimeMillis();
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        TaskOne taskOne = new TaskOne();
        Future futureOne = executorService.submit(taskOne);
        TaskTwo taskTwo = new TaskTwo();
        Future futureTwo = executorService.submit(taskTwo);
        executorService.shutdown();
        System.out.println("main thread is running");
        System.out.println("TaskTwo return value = " + futureTwo.get());
        System.out.println("TaskOne return value = " + futureOne.get());
        System.out.println("main thread finished");
        long endTime = System.currentTimeMillis();
        System.out.println("use time = " + (endTime - startTime) + "ms");
    }

    static class TaskOne implements Callable {
        @Override
        public String call() throws Exception {
            Thread.sleep(1000);
            System.out.println("TaskOne thread is running");
            return "hello TaskOne";
        }
    }

    static class TaskTwo implements Callable {
        @Override
        public String call() throws Exception {
            Thread.sleep(2000);
            System.out.println("TaskTwo thread is running");
            return "hello TaskTwo";
        }
    }
}

执行结果:

main thread is running
TaskOne thread is running
TaskTwo thread is running
TaskTwo return value = hello TaskTwo
TaskOne return value = hello TaskOne
main thread finished
use time = 2012ms

代码示例3中有两个Task,,可以看出,新增Task,对原先代码的改动量并不大,逻辑很清晰。
这段代码通过继承Thread或者实现Runnable创建线程,配合使用CountDownLatch类,再定义一些共享变量,同样可以实现,看下面这段代码,CountDownLatch类有什么用,读者可以自己去谷歌。

package com.async.future;

import java.util.concurrent.CountDownLatch;

/**
 * Created by linbo on 2018/5/1.
 */
class Task implements Runnable {

    private ShareVariable shareVariable;
    private int count;
    private String threadName;
    private CountDownLatch countDownLatch;

    public Task(ShareVariable shareVariable, int count, String threadName, CountDownLatch countDownLatch) {
        this.shareVariable = shareVariable;
        this.count = count;
        this.threadName = threadName;
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1000 * count);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(threadName + " thread is running");
        shareVariable.setInfo("hello " + threadName);
        countDownLatch.countDown();
    }
}

class ShareVariable {
    private String info;

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}

public class RunnableImpl {
    public static void main(String[] args) throws InterruptedException {
        long startTime = System.currentTimeMillis();
        CountDownLatch countDownLatch = new CountDownLatch(2);
        ShareVariable shareVariable1 = new ShareVariable();
        ShareVariable shareVariable2 = new ShareVariable();
        new Thread(new Task(shareVariable1, 1, "TaskOne", countDownLatch)).start();
        new Thread(new Task(shareVariable2, 2, "TaskTwo", countDownLatch)).start();
        System.out.println("main thread is running");
        countDownLatch.await();
        System.out.println("TaskOne return value = " + shareVariable1.getInfo());
        System.out.println("TaskTwo return value = " + shareVariable2.getInfo());
        System.out.println("main thread finished");
        long endTime = System.currentTimeMillis();
        System.out.println("use time = " + (endTime - startTime) + "ms");
    }
}

执行结果:

main thread is running
TaskOne thread is running
TaskTwo thread is running
TaskOne return value = hello TaskOne
TaskTwo return value = hello TaskTwo
main thread finished
use time = 2006ms

可以看出实现相同的逻辑,使用Thread或者Runnable要复杂一些。

你可能感兴趣的:(Future、Callable和FutureTask)