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

不清楚任务是否支持取消

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


目录
相关文章
|
Java
线程池是什么?线程池在实际工作中的应用
总的来说,线程池是一种有效的多线程处理方式,它可以提高系统的性能和稳定性。在实际工作中,我们需要根据任务的特性和系统的硬件能力来合理设置线程池的大小,以达到最佳的效果。
315 18
|
Java 程序员
Java社招面试中的高频考点:Callable、Future与FutureTask详解
大家好,我是小米。本文主要讲解Java多线程编程中的三个重要概念:Callable、Future和FutureTask。它们在实际开发中帮助我们更灵活、高效地处理多线程任务,尤其适合社招面试场景。通过 Callable 可以定义有返回值且可能抛出异常的任务;Future 用于获取任务结果并提供取消和检查状态的功能;FutureTask 则结合了两者的优势,既可执行任务又可获取结果。掌握这些知识不仅能提升你的编程能力,还能让你在面试中脱颖而出。文中结合实例详细介绍了这三个概念的使用方法及其区别与联系。希望对大家有所帮助!
715 60
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
932 6
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
347 8
|
数据采集 存储 数据处理
Python中的多线程编程及其在数据处理中的应用
本文深入探讨了Python中多线程编程的概念、原理和实现方法,并详细介绍了其在数据处理领域的应用。通过对比单线程与多线程的性能差异,展示了多线程编程在提升程序运行效率方面的显著优势。文章还提供了实际案例,帮助读者更好地理解和掌握多线程编程技术。
|
监控 Java 数据库连接
Java线程管理:守护线程与用户线程的区分与应用
在Java多线程编程中,线程可以分为守护线程(Daemon Thread)和用户线程(User Thread)。这两种线程在行为和用途上有着明显的区别,了解它们的差异对于编写高效、稳定的并发程序至关重要。
423 2
|
存储 监控 安全
深入理解ThreadLocal:线程局部变量的机制与应用
在Java的多线程编程中,`ThreadLocal`变量提供了一种线程安全的解决方案,允许每个线程拥有自己的变量副本,从而避免了线程间的数据竞争。本文将深入探讨`ThreadLocal`的工作原理、使用方法以及在实际开发中的应用场景。
341 2
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
525 5

热门文章

最新文章