1.1Callable和Future和FutureTask创建过程
很多时候我们让多线程去帮我们处理事情,是需要拿到返回值的,有了异常也可以处理
注意callable可以有返回值,也可以抛出异常这点很关键。
1.1.2.callable自己实现多线程,但是没有返回值
package com.Li.Callables; import java.util.concurrent.Callable; /** * @Description: Callable接口的实现类 * @auther:Li Ya Hui * @Time:2021年4月22日下午7:18:09 */ public class Callableimpl implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println("当前线程的名字:"+Thread.currentThread().getName()); return null; } } package com.Li.Callables import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @Description: * @auther:Li Ya Hui * @Time:2021年4月23日下午4:02:41 */ public class Test { public static void main(String[] args) { //1.创建一个单线程化的线程池 ExecutorService single = Executors.newSingleThreadExecutor(); //2.callable 接口的实现类的实例化 Callableimpl callableimpl = new Callableimpl(); //3.将新创建的线程加入到线程池中 single.submit(callableimpl); } }
1.1.2通过callable+future实现多线和获取结果
package com.Li.Callables; import java.util.concurrent.Callable; /** * @Description: Callable接口的实现类 * @auther:Li Ya Hui * @Time:2021年4月22日下午7:18:09 */ public class Callableimpl implements Callable<Integer> { //定义一个变量,用于承接线程任务执行之后的结果 public int sum = 0; @Override //重写callable接口方法 public Integer call() throws Exception { System.out.println("当前线程的名字:"+Thread.currentThread().getName()); System.out.println("通过实现callable接口的创建的多线程开始执行了"); //模拟一个耗时的感觉 Thread.sleep(1000); for (int i = 0; i < 100; i++) { sum+=i; } System.out.println("通过实现callable接口的创建的多线程结束执行了"); return null; } } package com.Li.Callables; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * @Description: 测试通过callable+future实现多线程和获取结果 * @auther:Li Ya Hui * @Time:2021年4月23日下午4:02:41 */ public class Test { public static void main(String[] args) throws InterruptedException, ExecutionException { //1.创建一个单线程化的线程池 ExecutorService single = Executors.newSingleThreadExecutor(); // ExecutorService single = Executors.newFixedThreadPool(5); // ExecutorService single = Executors.newSingleThreadExecutor(); // ExecutorService single = Executors.newScheduledThreadPool(1); //2.callable 接口的实现类的实例化 Callableimpl callableimpl = new Callableimpl(); //3.将新创建的线程加入到线程池中 //single.submit(callableimpl); Future<Integer> future = single.submit(callableimpl); //4.关闭线程池 single.shutdown(); if(single.isShutdown()) { System.out.println("线程池已关闭"); } //模拟主函数还存在 Thread.sleep(3000); System.out.println("主函数线程还存在"); //5.取出新的线程执行完之后的结果 if ((future.get())!=null) { System.out.println(future.get()); }else { System.out.println("future.get()获取到的结果为null"); } if(future.isDone()) { System.out.println("线程池中的任务已经完成!"); } System.out.println("主函数线程结束了"); } }
1.1.3通过callable+future实现多线程,和获取结果和抛出异常
package com.Li.Callables; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; /** * @Description: callable的实现类 callable+future实现多线程,和获取结果和抛出异常 * @auther:Li Ya Hui * @Time:2021年4月23日下午8:21:58 */ public class Callable02 implements Callable<String>{ //定义一个变量用来表示现成的不同用途 public int flag; //构造方法 传flag public Callable02(int a ) { super(); this.flag = a; } @Override public String call() throws Exception { //三种运行任务 第一种正常 if (flag == 0) { return "flag == 0"; } //三种运行任务 第二种死循环 if (flag == 1 ) { try { while (true) { System.out.println("死循环....."); Thread.sleep(2000); } } catch (InterruptedException e) { System.out.println("中断异常"); } return "false "; } //三种运行任务 第三种会报错 else { throw new Exception("输入有误"); } } } package com.Li.Callables; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * @Description: 测试类,测试callable+future 实现多线程和获取结果,抛出异常 * @auther:Li Ya Hui * @Time:2021年4月23日下午9:18:47 */ public class Test02 { public static void main(String[] args) throws InterruptedException, ExecutionException { //1.创建三个新的线程 Callable02 task1= new Callable02(0); Callable02 task2 = new Callable02(1); Callable02 task3 = new Callable02(2); //2.创建线程池 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); //3.将这三个线程逐次装入线程池中 Future<String> future = fixedThreadPool.submit(task1); System.out.println(future.get()); Future<String> future2 = fixedThreadPool.submit(task2); // System.out.println(future2.get());// 不能获取返回值,因为会造成一直等待结果,线程堵塞 //假定5秒钟之后 结束任务2 Thread.sleep(5000); future2.cancel(true);//直接取消线程任务(TRUE) 正常执行 System.out.println(future2); //如果报错,catch try { Future<String> future3 = fixedThreadPool.submit(task3); System.out.println(future3.get()); } catch (Exception e) { System.out.println(e.getMessage()); } //关闭线程池 fixedThreadPool.shutdown(); } }
1.1.4测试通过callable+future实现多线程和获取结果
package com.Li.Callables; import java.util.concurrent.Callable; /** * @Description: Callable接口的实现类 * @auther:Li Ya Hui * @Time:2021年4月22日下午7:18:09 */ public class Callable03 implements Callable<Integer> { //定义一个变量,用于承接线程任务执行之后的结果 public int sum = 0; @Override //重写callable接口方法 public Integer call() throws Exception { System.out.println("当前线程的名字:"+Thread.currentThread().getName()); System.out.println("通过实现callable接口的创建的多线程开始执行了"); //模拟一个耗时的感觉 Thread.sleep(1000); for (int i = 0; i < 100; i++) { sum+=i; } System.out.println("通过实现callable接口的创建的多线程结束执行了"); return sum; } } package com.Li.Callables; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; /** * @Description: 测试通过callable+future实现多线程和获取结果 * @auther:Li Ya Hui * @Time:2021年4月23日下午4:02:41 */ public class Test3 { public static void main(String[] args) throws InterruptedException, ExecutionException { //1.创建一个单线程化的线程池 ExecutorService single = Executors.newSingleThreadExecutor(); //2.Call接口的实现类的实例化 Callable03 callable03 = new Callable03(); //将call able接口的实现的实现对象传入Future Task的构造器中获取Future Task的实例化对象 FutureTask<Integer> futureTask = new FutureTask<Integer>(callable03); //4.将Future Task的实例化对象加入到线程池中并执行 single.submit(futureTask); //5.关闭线程池 single.shutdown(); //模拟出主函数线程还存话 Thread.sleep(3000); System.out.println("主函数线程还存在"); if ((futureTask.get())!=null) { System.out.println(futureTask.get()); }else { System.out.println("新的结果为:"+futureTask.get()); } //6.通过Future task获取线程执行之后的结果 System.out.println("主函数线程结束了"); } }
2.1.Future简介
Future接口用于获取异步计算的结果,可通过get()获取结果、cancel()取消、isDone()判断是否完成等操作。
- V get(): 获取结果,若无结果会阻塞至异步计算完成
- V get(long timeOut, TimeUnit unit):获取结果,超时返回null
- boolean isDone():执行结束(完成/取消/异常)返回true
- boolean isCancelled():任务完成前被取消返回true
- boolean cancel(boolean mayInterruptRunning):取消任务,未开始或已完成返回false,参数表示是否中断执行中的线程
2.1 cancel()中的false参数
如上面所介绍的,传入true会中断线程停止任务,传入false则会让线程正常执行至完成,刚开始我难以理解传入false的作用,既然不会中断线程,那么这个cancel方法不就没有意义了吗?后来查阅了许多资料,在stackoverflow上找到了一个比较好的解释,终于恍然大悟。
简单来说,传入false参数只能取消还没有开始的任务,若任务已经开始了,就任由其运行下去。
当创建了Future实例,任务可能有以下三种状态:
- 等待状态。此时调用cancel()方法不管传入true还是false都会标记为取消,任务依然保存在任务队列中,但当轮到此任务运行时会直接跳过。
- 完成状态。此时cancel()不会起任何作用,因为任务已经完成了。
- 运行中。此时传入true会中断正在执行的任务,传入false则不会中断。
3.1.一句话说明白shutdown和shutdownNow的区别
- shutdown只是将线程池的状态设置为SHUTWDOWN状态,正在执行的任务会继续执行下去,没有被执行的则中断。
- 而shutdownNow则是将线程池的状态设置为STOP,正在执行的任务则被停止,没被执行任务的则返回。
举个工人吃包子的例子,一个厂的工人(Workers)正在吃包子(可以理解为任务),假如接到shutdown的命令,那么这个厂的工人们则会把手头上的包子给吃完,没有拿到手里的笼子里面的包子则不能吃!而如果接到shutdownNow的命令以后呢,这些工人们立刻停止吃包子,会把手头上没吃完的包子放下,更别提笼子里的包子了。
3.2线程池过程:
1.当创建线程池后,初始时,线程池处于RUNNING状态,此时线程池中的任务为0;
2.如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;
3.如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;
4.当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。接着会执行terminated()函数。
5.线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED,线程池被设置为TERMINATED状态
4.cancel()总结
Future.cancel(true)适用于:
长时间处于运行的任务,并且能够处理interruption
Future.cancel(false)适用于:
未能处理interruption的任务
不清楚任务是否支持取消
需要等待已经开始的任务执行完成