Callable、Future、FutureTask在多线程中的应用场景

简介: Callable、Future、FutureTask在多线程中的应用场景

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的任务

不清楚任务是否支持取消

需要等待已经开始的任务执行完成


目录
相关文章
|
7月前
|
存储 Java
高并发编程之多线程锁和Callable&Future 接口
高并发编程之多线程锁和Callable&Future 接口
89 1
|
4月前
|
并行计算 Java 大数据
Callable和Future
Callable和Future
|
2月前
|
Java C++
【多线程】JUC的常见类,Callable接口,ReentranLock,Semaphore,CountDownLatch
【多线程】JUC的常见类,Callable接口,ReentranLock,Semaphore,CountDownLatch
37 0
|
5月前
|
消息中间件 缓存 NoSQL
Redis快速度特性及为什么支持多线程及应用场景
Redis快速度特性及为什么支持多线程及应用场景
127 11
|
4月前
|
Java API 调度
JUC线程池: FutureTask详解
总而言之,FutureTask是Java并发编程中一个非常实用的类,它在异步任务执行及结果处理方面提供了优雅的解决方案。在实现细节方面可以搭配线程池的使用,以及与Callable接口的配合使用,来完成高效的并发任务执行和结果处理。
46 0
|
5月前
|
存储 缓存 安全
(八)深入并发之Runnable、Callable、FutureTask及CompletableFuture原理分析
关于Runnable、Callable接口大家可能在最开始学习Java多线程编程时,都曾学习过一个概念:在Java中创建多线程的方式有三种:继承Thread类、实现Runnable接口以及实现Callable接口。但是实则不然,真正创建多线程的方式只有一种:继承Thread类,因为只有`new Thread().start()`这种方式才能真正的映射一条OS的内核线程执行,而关于实现Runnable接口以及实现Callable接口创建出的Runnable、Callable对象在我看来只能姑且被称为“多线程任务”,因为无论是Runnable对象还是Callable对象,最终执行都要交由Threa
107 1
|
5月前
|
消息中间件 安全 Java
线程和进程的区别及应用场景
线程和进程的区别及应用场景
|
5月前
|
消息中间件 安全 Java
线程和进程的区别及应用场景
线程和进程的区别及应用场景
|
5月前
|
监控 Java 开发者
Java面试题:解释Java内存模型中的内存顺序规则,Java中的线程组(ThreadGroup)的工作原理,Java中的FutureTask的工作原理
Java面试题:解释Java内存模型中的内存顺序规则,Java中的线程组(ThreadGroup)的工作原理,Java中的FutureTask的工作原理
31 0
|
5月前
|
存储 算法 Java
Java面试题:详细描述Java堆内存的垃圾回收过程,解释Java中的线程池(ThreadPool)的工作原理,解释Java中的FutureTask的工作原理
Java面试题:详细描述Java堆内存的垃圾回收过程,解释Java中的线程池(ThreadPool)的工作原理,解释Java中的FutureTask的工作原理
35 0