通常来讲,我们使用Runnable
接口或者Thread
类来创建一个新线程,但是他们有一个缺点,就是run
方法没有返回值,如果我们需要开启线程后获得一个返回值,就可以使用Callable
接口配合Future
类来实现。
一、Callable
接口
Callable
⼀般是配合线程池⼯具ExecutorService
来使⽤。ExecutorService
可以使⽤submit
⽅法来让⼀个Callable
接⼝执⾏,它会返回⼀个Future
,我们后续的程序可以通过这个Future
的get
⽅法得到结果。
可以看一下下面的Demo
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class FutureDemo implements Callable<String> {
@Override
public String call() throws Exception {
return "Hello,World";
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
FutureDemo futureDemo = new FutureDemo();
Future<String> tFuture = executorService.submit(futureDemo);
try {
System.out.println(tFuture.get());
} catch (Exception exception) {
}
}
}
运行后,控制台会输出
Hello,World
二、Future
接口
看一下Future
接口的源代码
public interface Future<V> {
/**
* 试图取消一个线程的执行,但是不一定能成功,因为任务可能已完成或已取消或其他因素不能取消
*
* @param 是否允许打断正在执行的任务
* @return 是否取消成功
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
* 获取线程是否已取消
*
* @return 线程完成前被取消返回true
*/
boolean isCancelled();
/**
* 获取线程是否已完成
*
* 如果线程异常、被终止或者取消,都会返回true
*
* @return 线程完成返回true
*/
boolean isDone();
/**
* 获取线程返回值
*
* @return 线程返回结果
*/
V get() throws InterruptedException, ExecutionException;
/**
* 在给定时间内获取返回值,如果超时未获取到返回超时异常
*
* @param 获取结果最大等待时间
* @param 时间计量单位
* @return 结果
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
下面通过实际代码看一下Future
接口的基本用法。
修改上面代码如下
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class FutureDemo implements Callable<String> {
@Override
public String call() throws Exception {
return "Hello,World";
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
FutureDemo futureDemo = new FutureDemo();
Future<String> tFuture = executorService.submit(futureDemo);
// 判断线程是否完成
System.out.println("线程是否完成:" + tFuture.isDone());
// 判断线程是否取消
System.out.println("线程是否取消:" + tFuture.isCancelled());
try {
System.out.println(tFuture.get());
// 判断线程是否完成
System.out.println("线程是否完成:" + tFuture.isDone());
// 判断线程是否取消
System.out.println("线程是否取消:" + tFuture.isCancelled());
} catch (Exception exception) {
}
}
}
输出结果
线程是否完成:false
线程是否取消:false
Hello,World
线程是否完成:true
线程是否取消:false
可以看到,在获取到结果前,线程是未完成、未取消的,获取到结果后,线程正常结束,此时线程是已完成、未取消的。
下面代码继续修改,我们在获取结果之前先取消,因为线程可能来不及取消已经完成,所以我们加个让call
方法睡眠1s
。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.CancellationException;
public class FutureDemo implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "Hello,World";
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
FutureDemo futureDemo = new FutureDemo();
Future<String> tFuture = executorService.submit(futureDemo);
// 判断线程是否完成
System.out.println("线程是否完成:" + tFuture.isDone());
// 判断线程是否取消
System.out.println("线程是否取消:" + tFuture.isCancelled());
try {
// 试图取消线程
boolean isCancelled = tFuture.cancel(true);
System.out.println("任务是否取消成功:" + isCancelled);
// 判断线程是否完成
System.out.println("线程是否完成:" + tFuture.isDone());
// 判断线程是否取消
System.out.println("线程是否取消:" + tFuture.isCancelled());
// get的时候需要判断线程状态
System.out.println(tFuture.get());
} catch (CancellationException cancellationException) {
System.out.println("线程已经被取消");
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
输出结果
线程是否完成:false
线程是否取消:false
任务是否取消成功:true
线程是否完成:true
线程是否取消:true
线程已经被取消
可以看到,线程已经取消成功,线程取消成功后,如果还是调用get
方法,会抛出CancellationException
异常,并且线程取消成功后,isDone
方法也会返回true
,代表线程已经完成。
最后一个方法,我们看一下增加get
的重载方法。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.CancellationException;
public class FutureDemo implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "Hello,World";
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
FutureDemo futureDemo = new FutureDemo();
Future<String> tFuture = executorService.submit(futureDemo);
try {
// get方法如果0.5秒内未获取到结果就抛出超时异常
System.out.println(tFuture.get((long)0.5, TimeUnit.SECONDS));
} catch (TimeoutException timeoutException) {
System.out.println("超过0.5s未获取到结果");
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
输出结果
超过0.5s未获取到结果
三、FutureTask
类
FutureTask
是Java自带的Future
接口的实现类。FutureTask
是实现的RunnableFuture
接⼝,⽽RunnableFuture
接⼝同时继承了Runnable
接⼝和`Future接⼝。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.CancellationException;
public class FutureDemo implements Callable<String> {
@Override
public String call() throws Exception {
return "Hello,World";
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
FutureDemo futureDemo = new FutureDemo();
FutureTask<String> futureTask = new FutureTask<>(futureDemo);
executorService.submit(futureTask);
try {
System.out.println(futureTask.get());
}catch (Exception exception) {
exception.printStackTrace();
}
}
}
输出结果:
Hello,World
文章摘抄自《深入浅出Java多线程.pdf》并对内容进行完善。
评论 (0)