Callable
由关系图可知,Callable和Runnable一样,也是一个函数式接口,可以使用Lambda表达式
与之不同的是,其内部的call()方法可以抛出异常且能return一个返回值
Callable<Object> callable = new Callable() { @Override public Object call() throws Exception { System.out.println("执行call方法"); return null; } };
FutureTask
FutureTask构造方法源码: public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable }
Future接口方法
//mayInterruptIfRunning:是否取消正在执行但还有执行完毕的任务 //true:取消任务 返回true //false:让任务执行完毕 返回false boolean cancel(boolean mayInterruptIfRunning); //任务是否被取消 boolean isCancelled(); //任务是否完成 //完成可能是由于正常终止、异常或取消 --- 在所有这些情况下,此方法将返回 true boolean isDone(); //得到任务返回值,如果未执行的话会阻塞等待 V get() throws InterruptedException, ExecutionException; //设置最大阻塞时间,如果超过最大阻塞时间后还是没有执行完任务,抛出TimeOutException V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
根据上方关系图我们可知
FutureTask可以包装Runnable/Callable对象-----构造方法
FutureTask实现了Runnable接口 ,故其可以作为Runnable被线程执行
notice:线程执行的是FutureTask类包装的Callable对象下的call()方法
public static void main(String[] args) throws ExecutionException, InterruptedException { //使用Callable进行1-100相加数和 Callable<Long> callable = new Callable<Long>() { @Override public Long call() throws Exception { System.out.println("开始执行子线程相加内容"); long sum = 0; for(int i = 1; i <= 100; i++){ sum += i; } return sum; } }; FutureTask<Long> futureTask = new FutureTask<>(callable); Thread t = new Thread(futureTask); //真正执行call方法的是线程 所以必须要启动线程后 call方法才会执行 //t.start(); //相当于t.join操作 System.out.println("futureTask.get()得到call方法的函数返回值:"+futureTask.get()); //如果没有call方法内容没有执行完 get方法会一直阻塞 }
此时没有执行t.start操作,futureTask.get()方法就会一直阻塞
反之执行t.start;
//使用Callable进行1-100相加数和 Callable<Long> callable = new Callable<Long>() { @Override public Long call() throws Exception { Thread.sleep(3000); System.out.println("开始执行子线程相加内容"); long sum = 0; for(int i = 1; i <= 100; i++){ sum += i; } return sum; } }; FutureTask<Long> futureTask = new FutureTask<>(callable); Thread t = new Thread(futureTask); //真正执行call方法的是线程 所以必须要启动线程后 call方法才会执行 t.start(); long begin = System.currentTimeMillis(); //相当于t.join操作 System.out.println("futureTask.get()得到call方法的函数返回值:"+futureTask.get()); //如果没有call方法内容没有执行完 get方法会一直阻塞 long end = System.currentTimeMillis(); System.out.println("阻塞时间:"+ (end -begin)); }
上述代码中,我们在call方法中休眠了三秒,由于当get方法任务没有执行完时,就会阻塞,故主线程被三秒,所以get()操作类似于join方法
get():
我们知道,当线程在休眠时被终止时,会触发sleep内部异常,在Runnable中,触发异常后并不会终止进程,而是将线程提前唤醒,且将终止标志位重新设置为false,后续的操作由程序员的代码决定
public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { for(int i = 0; i < 3; i++){ System.out.println("Hello World"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("触发skeep interrupted异常 后续执行操作由我决定"); } } } }); t1.start(); t1.interrupt(); System.out.println("线程是否被终止:"+t1.isInterrupted()); }
即使触发了异常,但还是继续完成了runnable中的run方法
但在Callable中,如果线程被休眠时被唤醒,FutureTask.get()方法就会直接报错且退出程序
public static void main(String[] args) throws ExecutionException, InterruptedException { Callable<String> callable = new Callable<String>() { @Override public String call() throws Exception { System.out.println("你好"); Thread.sleep(1000); return "我要报错拉"; } }; FutureTask task = new FutureTask(callable); Thread t = new Thread(task); t.start(); t.interrupt(); //非正常终止 --- 休眠时终止 System.out.println("isDone:"+task.isDone()); System.out.println(task.get()); }
但如果仅仅是启动线程(没有FutureTask.get()操作),那么不会报错,但会直接终止线程
public static void main(String[] args) throws ExecutionException, InterruptedException { Callable<String> callable = new Callable<String>() { @Override public String call() throws Exception { for(int i = 0; i < 3; i++){ System.out.println("你好"); Thread.sleep(1000); } return "我要报错拉"; } }; FutureTask task = new FutureTask(callable); Thread t = new Thread(task); t.start(); t.interrupt(); //非正常终止 --- 休眠时终止 System.out.println("isDone:"+task.isDone()); }
超时get():
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException { //超时get方法 Callable<String> callable = new Callable<String>() { @Override public String call() throws Exception { Thread.sleep(2000); System.out.println("任务继续执行"); return "ok"; } }; FutureTask futureTask = new FutureTask(callable); Thread t = new Thread(futureTask); t.start(); //若超出时间 报TimeoutException异常 call方法中的内容继续进行 //无法取到call方法中的返回值了 System.out.println(futureTask.get(1, TimeUnit.SECONDS)); }
.
超时后call方法中的任务还是会继续进行,但无法通过get方法获得call方法的返回值了
cancel():
public static void main(String[] args) { Callable<Integer> callable = new Callable<Integer>() { @Override public Integer call() throws Exception { int sum = 0; for(int i = 0; i <= 10; i++){ sum += i; } return sum; } }; FutureTask<Integer> futureTask = new FutureTask<>(callable); Thread t = new Thread(futureTask); t.start(); //取消任务 参数mayInterruptIfRunning是否取消正在运行的任务 其内部实际上还是通过interrupt方法 System.out.println("cancel:"+futureTask.cancel(true)); //任务是否被取消 System.out.println("isCancelled:"+futureTask.isCancelled()); //任务是否执行完成 //如果此任务已完成,则返回 true。完成可能是由于正常终止、异常或取消 -- 在所有这些情况下,此方法将返回 true System.out.println("isDone:"+futureTask.isDone()); }
//取消任务 参数mayInterruptIfRunning是否取消正在运行的任务 其内部实际上还是通过interrupt方法 System.out.println("cancel:"+futureTask.cancel(true)); //在取消任务后 调用get方法 此时不能返回call方法返回值 且会抛出异常 退出程序 System.out.println("after cancel:"+ futureTask.get());
使用线程池执行Callable
//利用线程池执行 public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService service = Executors.newFixedThreadPool(10); //execute Runnable command //submit Future<V> future //这里是Callable 只能使用submit Runnable/Callable都可以 返回值是Future<?> / 不能使用execute:Runnable Future<String> future = service.submit(new Callable<String>() { @Override public String call() throws Exception { return "Hello World"; } }); System.out.println(future.get()); service.shutdown(); }