欢迎微信搜索并关注“小猴子的技术笔记”公众号 私信我 领取丰富的视频学习资料!
开发业务中有很多用到线程的场景。遇到比较耗时的场景往往会起一个线程进行执行,如果不返回结果就可以直接new一个Thread对业务进行操作。因为runnable不会返回结果,也不会抛出异常,如果需要拿到返回值的话则需要使用Callable和Future或者Future Task进行结合。
这里可以简单理解为“Callable”表示产生结果和抛出异常。而“Future”是一个接口,表示异步计算结果
“Callable”是一个泛型接口,里面只有一个方法“call()”可以用来计算产生结果,如果无法执行就抛出异常。
@FunctionalInterface
public interface Callable<V> {
// 结算结果。如果无法执行则抛出异常
V call() throws Exception;
}
“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;
}
“boolean cancel(boolean mayInterruptedRunning)”:尝试取消执行此任务,如果任务已经完成,则调用返回false。如果任务已经启动,并且“mayInterruptedRunning”设置为true,那么将以中断执行此任务线程的方式来试图停止任务,成功返回true,失败返回false。
“boolean isCancelled()”:如果任务完成前被取消则返回true。
“boolean isDone()”:无论是任务正常结束还是中途被中断或者由于其他原因而结束,都将返回true。
“V get()”:获取计算结果,如果计算还没有完成就调用了这个方法那么线程将会阻塞等待获取计算完成。
“V get(long timeout, TimeUnit unit)”:获取计算结果,如果计算还没有完成就调用了这个方法,那么线程将会等待设置的时间内会抛出异常。如果在获取等待的过程中遇到了线程的中断、取消、线程池等异常也是会被捕获到。
因为Future是一个接口,没有办法被实例化,因此需要使用FutureTask进行类的创建。
public class FutureTask<V> implements RunnableFuture<V> {
// 省略了一下方法
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
先看一个Callable和Future结合运行的例子:
public class MyCallable implements Callable<String> {
private int sleepTime;
private String msg;
public MyCallable(int sleepTime, String msg) {
this.sleepTime = sleepTime;
this.msg = msg;
}
@Override
public String call() throws Exception {
TimeUnit.SECONDS.sleep(sleepTime);
System.out.println("线程休眠了:" + sleepTime + "秒执行完毕");
return msg;
}
}
然后进行测试,测试代码如下:
public class FutureTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();
System.out.println("~~~~~~~~~~~~~开始进行业务的计算~~~~~~~~~~~~~~");
Future<String> first = executor.submit(new MyCallable(3, "hello!"));
Future<String> second = executor.submit(new MyCallable(2, "可爱的小猴子,要加油哦!"));
// 主线程可以继续执行一些其他的任务
System.out.println("~~~~~~~主线程可以继续执行一些其他的任务~~~~~~~");
System.out.println("最后的结果:" + first.get() + " " + second.get());
executor.shutdown();
}
}
最后的运行结果:
~~~~~~~~~~~~~开始进行业务的计算~~~~~~~~~~~~~~
~~~~~~~主线程可以继续执行一些其他的任务~~~~~~~
线程休眠了:2秒执行完毕
线程休眠了:3秒执行完毕
最后的结果:hello!可爱的小猴子,要加油哦!
如果使用FutureTask的话,则代码的示例如下:
public class FutureTaskTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();
System.out.println("~~~~~~~future task 开始进行业务的计算~~~~~~~~~");
FutureTask<String> first = new FutureTask<>(new MyCallable(2, "加油!"));
FutureTask<String> second = new FutureTask<>(new MyCallable(5, "努力的人儿!"));
executor.submit(first);
executor.submit(second);
executor.shutdown();
System.out.println("~~~~~~~主线程可以继续执行一些其他的任务~~~~~~~");
System.out.println("最后的结果:" + first.get() + " " + second.get());
}
}
运行结果:
~~~~~~~future task 开始进行业务的计算~~~~~~~~~
~~~~~~~主线程可以继续执行一些其他的任务~~~~~~~
线程休眠了:2秒执行完毕
线程休眠了:5秒执行完毕
最后的结果:加油!努力的人儿!
欢迎微信搜索并关注“小猴子的技术笔记”公众号 私信我 领取丰富的视频学习资料!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。