我们知道创建一个线程有两种方式,一种是实现Thread类,一种是实现Runnable接口,这两种方法都有一个缺点,就是没法拿到线程的执行结果,一个变通的方法,就是使用共享变量,间接的返回线程的执行结果。在JDK1.5之后,java.util.concurrent包中提供了两个新的接口:Future和Callable。
1、Callable接口
先看Callable接口,Callable类似于Runnable接口,通过实现Callable接口,也可以自定义一个线程。Callable接口一般和ExecutorService配合使用,ExecutorService有一个submit方法,submit方法有三个重载版本:
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
2、Future接口
从上面我们知道ExecutorService 的submit方法的返回值是Future接口,Future接口如下所示:
public interface Future<V> {
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<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>
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<String> 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<String> {
@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<String> futureTask = new FutureTask<String>(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<String> {
@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<String> futureOne = executorService.submit(taskOne);
TaskTwo taskTwo = new TaskTwo();
Future<String> 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<String> {
@Override
public String call() throws Exception {
Thread.sleep(1000);
System.out.println("TaskOne thread is running");
return "hello TaskOne";
}
}
static class TaskTwo implements Callable<String> {
@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要复杂一些。