本文已参与「新人创作礼」活动,一起开启掘金创作之路。
@[TOC]
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的任务
- 不清楚任务是否支持取消
- 需要等待已经开始的任务执行完成