线程池的创建和使用

  • 常见面试题
    线程池中的execute和submit方法都是用于向线程池提交任务以异步执行,但它们之间存在一些关键的区别,主要包括返回值、异常处理、方法重载以及适用场景等方面。以下是对这些区别的详细解析:
  1. 返回值
    • execute方法:此方法只接受Runnable类型的任务,并且没有返回值。
      一旦任务被提交给线程池,execute方法会立即返回,而不会等待任务执行完成。
    • submit方法:此方法更为灵活,可以接受Runnable或Callable类型的任务。
      对于Runnable任务,submit方法会返回一个Future<?>对象,
      虽然这个Future对象的get()方法调用通常会返回null,但它仍然可以用于检查任务是否完成。
      对于Callable任务,submit方法会返回一个Future对象,
      其中T是Callable任务返回结果的类型,通过调用Future对象的get()方法可以获取到任务执行的结果。
  2. 异常处理
    • execute方法:如果任务在执行过程中抛出异常,这个异常不会被execute方法直接抛出,而是会被捕获并记录。
      通常,这些异常会被传递给线程池的未捕获异常处理器(UncaughtExceptionHandler)进行处理。
    • submit方法:对于submit方法提交的任务,
      如果任务执行过程中抛出异常,这个异常会被包装在ExecutionException中,
      并在调用Future对象的get()方法时重新抛出。这使得开发者能够更容易地捕获和处理这些异常。
  3. 方法重载
    • execute方法:该方法没有重载形式,只接受一个Runnable类型的参数。
    • submit方法:submit方法有多种重载形式,可以接受Runnable、Callable或其他类型的任务作为参数,
      并返回相应的Future对象。这使得submit方法在处理不同类型任务时更加灵活。
  4. 适用场景
    • execute方法:适用于那些不需要关心任务执行结果和异常处理的场景。
      当开发者只是简单地将任务提交给线程池执行,而不关心任务何时完成或执行结果时,可以使用execute方法。
    • submit方法:适用于需要获取任务执行结果或进行异常处理的场景。
      当开发者需要等待任务完成并获取其结果,或者需要捕获和处理任务执行过程中抛出的异常时,应该使用submit方法。
      总结
      线程池中的execute和submit方法在返回值、异常处理、方法重载以及适用场景等方面存在显著的区别。开发者在选择使用哪个方法时,应根据具体的需求和场景进行权衡。一般来说,submit方法提供了更多的功能和灵活性,因此在需要获取任务执行结果或进行异常处理的场景中更为适用;而execute方法则更加简单直接,适用于那些不需要关心这些方面的场景。
  • 代码Demo

/**
 * 线程池配置
 */
@Component
public class ThreadPoolConfig {

    @Bean
    public ThreadPoolExecutor threadPool() {
        // 核心线程数,线程池中始终存活的线程数。
        int corePoolSize = 5;
        // 最大线程数,线程池中允许的最大线程数,当线程池的任务队列满了之后可以创建的最大线程数。
        int maximumPoolSize = 10;
        // 最大线程数可以存活的时间,当线程中没有任务执行时,最大线程就会销毁一部分,最终保持核心线程数量的线程。
        long keepAliveTime = 60;
        // 单位是和参数 maximumPoolSize 存活时间配合使用的,合在一起用于设定线程的存活时间。
        // 参数 keepAliveTime 的时间单位有以下 7 种可选:
        /*
            TimeUnit.DAYS:天
            TimeUnit.HOURS:小时
            TimeUnit.MINUTES:分
            TimeUnit.SECONDS:秒
            TimeUnit.MILLISECONDS:毫秒
            TimeUnit.MICROSECONDS:微妙
            TimeUnit.NANOSECONDS:纳秒
        */
        TimeUnit unit = TimeUnit.SECONDS;
        // 一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全。
        // 它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种,包含以下 7 种类型:
        /*
            ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
            LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
            SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保持它们。
            PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
            DelayQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素
            LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似,还含有非阻塞方法。
            LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
         */
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
        // 创建线程池
        return new ThreadPoolExecutor(
                corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }
}
  • 执行方法的逻辑
@Component
public class InvokeMethod {

    public Integer cacl(Integer num) {
        try {
            Thread.sleep(num * 1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return num;
    }

}
  • 模拟业务运行
@SpringBootTest
class MiaoApplicationTests {

    @Resource
    private ThreadPoolConfig threadPoolConfig;

    @Resource
    private InvokeMethod invokeMethod;

    @Test
    void contextLoads() throws Exception {
        // 获取线程池
        ThreadPoolExecutor threadPoolExecutor = threadPoolConfig.threadPool();
        List<Future<Integer>> futureList = Stream.of(1, 2, 3, 4, 5).map(item -> {
            return threadPoolExecutor.submit(() -> invokeMethod.cacl(item));
        }).collect(Collectors.toList());

        for (Future<Integer> future : futureList) {
            Integer resultNum = future.get();
            log.info("resultNum = {}", resultNum);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值