Callable的Future模式
一、线程实现方式
1.继承Thread类
2.实现Runnable接口
3.线程池
4.Callable
二、无论使用继承Thread类还是实现Runnable接口,还是使用线程池都没有办法解决2个问题
1.线程执行没有返回值结果
2.线程执行没有办法抛出异常,只能自己通过try-catch解决
三、Callable
在java中,创建线程一般有两种方式,一种是继承Thread类,一种是实现Runnable接口。然而,这两种方式的缺点是在线程任务执行结束后,无法获取执行结果。我们一般只能采用共享变量或共享存储以及线程通信的方式实现获得任务结果的目的;
不过,在java中,也提供了使用Callable和Future来实现获取任务结果的操作。Callable用来执行任务,产生结果,而Future用来获得结果;
@FunctionalInterface public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
四、Callable和Runnable的区别
1、Callable能接受一个泛型,然后在call方法中返回一个这个类型的值,而Runnable的run方法没有返回值;
2、Callable的call方法可以抛出异常,而Runnable的run方法不会抛出异常;
五、Future
Future莫斯的核心在于:去除了函数的等待时间,并使得原来需要等待的时间段可以用于处理其他业务逻辑;
Future模式:对于多线程,如果线程A要等待线程B的结果,那么线程A没有必要等待线程B,知道线程B有结果,可以先拿到一个未来的Future,等线程B有结果时再取真实的结果;
1、调用Callable的第一种实现方案
package com.zn.callableTest; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class MyCallable implements Callable<String> { @Override public String call() throws Exception { System.out.println("Callable接口中重写的Call方法,可以有返回值并且抛出异常"); return "callable"; } //调用Callable的第一种实现方案 public static void main(String[] args) throws ExecutionException, InterruptedException { MyCallable myCallable=new MyCallable(); //利用FutureTask执行Callable并且接受结果 FutureTask<String> stringFutureTask = new FutureTask<>(myCallable); //利用线程执行Task任务 new Thread(stringFutureTask).start(); //接受结果FutureTask.get会发生阻塞情况 System.out.println(stringFutureTask.get()); System.out.println("MyCallable执行完毕,返回值结果正确接收~"); } }
控制台效果:
2、调用Callable的第二种实现方案
package com.zn.callableTest; import java.util.concurrent.*; public class MyCallable implements Callable<String> { @Override public String call() throws Exception { System.out.println("Callable接口中重写的Call方法,可以有返回值并且抛出异常"); return "callable"; } //调用Callable的第二种实现方案 public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException { MyCallable myCallable=new MyCallable(); //创建一个线程池 ExecutorService executorService = Executors.newFixedThreadPool(3); //创建线程执行任务,接受任务结果 Future<String> future = executorService.submit(myCallable); //接受返回值 System.out.println(future.get(2000,TimeUnit.MILLISECONDS)); System.out.println("方式二,线程池:MyCallable执行完毕,返回值结果正确接收~"); //停止线程池 executorService.shutdown(); } }
控制台效果:
六、Future常用方法
V get():获取异步执行的结果,如果没有结果可用,此方法会阻塞知道异步计算完成;
V get(Long timeout,TimeUnit unit):获取异步执行结果,如果没哟结果可用,此方法会阻塞,但是会有时间限制,如果阻塞时间超过设定的timeout时间,该方法将抛出异常;
boolean isDone():如果任务执行结束,无论是正常结束或是中途取消还是发生异常,都返回true;
boolean isCanceller():如果任务完成前被取消,则返回true;
boolean cancel(boolean mayInterrupRunning):如果任务还没有开始,执行cancel方法将返回false;如果任务已经启动,执行cancel方法将以中断执行此任务线程的方式来试图停止任务,如果停止成功,返回true;
当任务已经启动,执行cancel(false)方法将不会对正在执行的任务线程产生影响(让线程正常执行到完成),此时返回false;
当任务已经启动,执行cancel方法将返回false,MayInterruptRunning参数表示是否中断执行中的线程;
实际上Future提供了三种功能:
- 能够中断执行中的任务;
- 判断任务是否执行完成;
- 获取任务执行完成后的结果;
七、Future提供三种功能
1.中断任务cancel(true)
package com.zn.callableTest; import java.util.concurrent.*; public class MyCallable implements Callable<String> { @Override public String call() throws Exception { System.out.println("Callable接口中重写的Call方法,可以有返回值并且抛出异常"); return "callable"; } public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException { MyCallable myCallable=new MyCallable(); //创建一个线程池 ExecutorService executorService = Executors.newFixedThreadPool(3); //创建线程执行任务,接受任务结果 Future<String> future = executorService.submit(myCallable); //中断任务 boolean cancel = future.cancel(true); if(cancel){ System.out.println("中断任务成功~"); }else{ //接受返回值 System.out.println(future.get(2000,TimeUnit.MILLISECONDS)); } //停止线程池 executorService.shutdown(); } }
控制台效果:
2.判断任务是否执行完成isDone()
3.获取任务执行后的结果get()
八、手写Future模式
package com.zn.callableTest; /** * 手写Future模式 */ public class MyFuture { //FLAG相当于数据标识,如果放入数据成功,则返回为true,否则返回为false private static boolean FLAG=false; private String data; public synchronized void setData(String data) throws InterruptedException { Thread.sleep(2000); //赋值操作 this.data = data; FLAG=true; //唤起 notify(); } public synchronized String getData() { //如果获取数据失败 if(!FLAG){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } return data; } public static void main(String[] args) { MyFuture future=new MyFuture(); new Thread(()->{ try { future.setData("张三"); System.out.println(future.getData()); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } }
控制台效果: