面试必问的线程池,一篇笔记搞定!

什么是线程池

首先我们要知道 new线程是有开销的,在栈里面需要开辟空间,针对这种有限的宝贵的资源,我们不能无限的去创建线程,这时候就有一个大聪明提出了线程池这么一个概念了,线程池可以帮我么保管起来,不需要去管什么时候new线程,什么时候销毁和回收。我们只需要往线程池里面提交任务即可。

要维护线程池我们需要做哪些事情

核心线程数

等待队列(不宜设置过长一般几百个就可以了,过长容易OOM)

最大线程

拒绝策略

线程工厂

空闲线程等待时间

时间单位

提交任务的流程有哪些?

当任务提交到线程池中的时候,首先是利用核心线程,当核心线程满了,那他回去等待队列,当等待队列也满了,会利用最大线程,当最大线程也满了,这时候会触发拒绝策略了

拒绝策略有哪些?

1. 丢弃任务并抛出异常(默认的)

2.退回到主线程继续执行(常用的策略,防止任务丢失)

3.丢弃任务但是不抛出异常

4.丢弃最早的未执行的任务,重新执行任务

线程异常的处理方案有哪些?

1.手动try-cath,手动捕获异常,打印日志进行补偿

2.submin提交后,Feature.get()接收异常信息

多线程的实现方式有哪些?

继承Thread类(重写run方法)

实现runable接口

实现calable接口和Future接口(通过重写call方法,这个是有返回值的,表示多线程的运行结果Future 它可以管理多线程运行的结果)

// 创建一个类实现 Callable 接口,这里要注意,Callable需要传递一个泛型,表示运行的结果类型
public class Thread03 implements Callable<Integer> {
 
    @Override
    public Integer call() throws Exception {
        // 随便写一段逻辑,求 1~100 的 整数和
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            sum = sum + i;
        }
        // 将计算的结果返回
        return sum;
    }
}

public class TestThread03 {
    public static void main(String[] args) {
        // 创建线程 Thread03的对象
        Thread03 t1 = new Thread03();
        // 将创建的 t1 对象作为参数传递给 FutureTask
        FutureTask<Integer> futureTask = new FutureTask<>(t1);
        // 创建 Thread 对象,并把 futureTask 作为参数的传递进去
        Thread thread = new Thread(futureTask);
 
        // 通过 start 启动线程
        thread.start();
 
        // 获取线程的结果,有异常使用 try。。。catch。。。
        try {
            Integer num = futureTask.get();
            System.out.println(num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

 线程池代码真实业务场景展示(多线程实现批量导出)

public class ExportConcurrentlyThreadPool {
    private final static String THREAD_IDENTIFICATION = "tfx-future-export-concurrently";
    public static ExecutorService executorService;

    static {
        int availProcessors = Runtime.getRuntime().availableProcessors();

        executorService = new TfxThreadPoolExecutor(availProcessors * 4, availProcessors * 4,
                60, TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(300), new TfxThreadFactory(THREAD_IDENTIFICATION),
                new ThreadPoolExecutor.CallerRunsPolicy());
    }
}
public Result<String> exportSpBrandQualificationList(SpBrandQualificationQryReq spBrandQualificationQryReq) {

        SpBrandQualificationQryParam param = new SpBrandQualificationQryParam();
        BeanUtils.copyProperties(spBrandQualificationQryReq, param);

        int pageSize = 100;
        param.setPageSize(pageSize);
        Long totalNum = spBrandQualificationFlowService.querySpBrandQualificationTotalNum(param);
        if (totalNum <= 0) {
            return Result.fail(ErrorCodeEnum.PARAM_INVALID.getCode(), "导出数据集为空");
        }
        if (totalNum > BrandQualificationSwitch.brandQualificationExportMaxLimit) {
            return Result.fail(ErrorCodeEnum.PARAM_INVALID.getCode(), "导出数据量过大,请缩小查询条件");
        }

        List<SpBrandQualificationEO> qualificationEOS = new ArrayList<>(totalNum.intValue());

        int pageCount = (int) Math.ceil(totalNum * 1.0 / pageSize);
        List<CompletableFuture<List<SpBrandQualificationEO>>> futureTasks = new ArrayList<>(pageCount);
        for (int pageIdx = 1; pageIdx <= pageCount; pageIdx++) {
            SpBrandQualificationQryParam finalParam = new SpBrandQualificationQryParam();
            BeanUtils.copyProperties(spBrandQualificationQryReq, finalParam);
            finalParam.setPageSize(pageSize);
            finalParam.setPageNo(pageIdx);
            String traceId = EagleEye.getTraceId();
            CompletableFuture<List<SpBrandQualificationEO>> future = CompletableFuture.supplyAsync(() -> {
                try {
                    long start = System.currentTimeMillis();
                    List<SpBrandQualificationDTO> spBrandQualificationDTOS = spBrandQualificationFlowService.querySpBrandQualificationList(finalParam);
                    List<Long> spUserIds = spBrandQualificationDTOS.stream().map(SpBrandQualificationDTO::getSpUserId).collect(Collectors.toList());
                    Multimap<Long, UserSettleDTO> uSerSettleDTOS = getUserSettleDTOMap(spUserIds);
                    List<SpBrandQualificationEO> result = convert2EOList(spBrandQualificationDTOS, uSerSettleDTOS);
                    log.info("subList cost: {}, traceId={}", System.currentTimeMillis() - start, traceId);
                    return result;
                } catch (Throwable throwable) {
                    log.error("exportSettleAuditList error, traceId={}", traceId, throwable);
                    return Lists.newArrayList();
                }
            }, ExportConcurrentlyThreadPool.executorService);
            futureTasks.add(future);
        }
        try {
            long start  = System.currentTimeMillis();
            CompletableFuture.allOf(futureTasks.toArray(new CompletableFuture[0])).get(12, TimeUnit.SECONDS);
            log.info("wait cost: {}, traceId={} ", System.currentTimeMillis() - start, EagleEye.getTraceId());
            for (CompletableFuture<List<SpBrandQualificationEO>> futureTask : futureTasks) {
                List<SpBrandQualificationEO> result = futureTask.get();
                qualificationEOS.addAll(result);
            }
        } catch (TimeoutException e) {
            log.error("exportBrandQualificationList timeout", e);
            return Result.fail(ErrorCodeEnum.SERVICE_TIME_OUT.getCode(), "导出超时,请缩小查询条件");
        } catch (Exception e) {
            log.error("exportBrandQualificationList error", e);
            return Result.fail(ErrorCodeEnum.SERVER_ERROR.getCode(), "导出异常,请重试");
        }

        String fileName = "品牌资质审核列表_" + IddContext.getUserName() + "_" + DateUtil.formatDate(new Date(), "yyyyMMddHHmmss");
        String url = excelHelper.publicExport(SpBrandQualificationEO.class, "供应商品牌资质审核数据", fileName,
                (excelExportUtil) -> excelExportUtil.exportModel(qualificationEOS, SpBrandQualificationEO.class));
        return Result.success(url);
    }
CompletableFuture多线程任务编排

代码解读:

CompletableFuture<List<SpBrandQualificationEO>> future = CompletableFuture.supplyAsync(() ->

这段代码的作用是

1.启动一个异步任务去获取(可能是从数据库、远程服务等)一个对象列表;

2.这个操作不会阻塞当前的执行线程。

3. 当需要使用这些数据时,可以通过future对象的其他方法(如thenApply, thenAccept, get等)来处理或获取这个异步计算的结果

CompletableFuture.allOf(futureTasks.toArray(new CompletableFuture[0])).get(12, TimeUnit.SECONDS);

等待futureTasks集合中的所有CompletableFuture任务在最多12秒内全部完成。

如果所有任务都在12秒内完成,则.get(12, TimeUnit.SECONDS)调用结束,程序继续执行。

如果有任何任务超过12秒未完成,则会抛出TimeoutException

这是一种控制异步操作整体超时的有效方式。

利用线程池异步调用第三方接口案例

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class AsyncThirdPartyApiCaller {

    public static void main(String[] args) {
        // 创建一个固定大小的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        // 模拟需要异步调用的第三方API任务
        Future<String> resultFuture = executorService.submit(() -> callThirdPartyApi("someData"));

        // 主线程可以继续执行其他任务,不必等待resultFuture完成

        // 以下代码为模拟主线程其他任务
        System.out.println("主线程继续执行其他任务...");

        // 如果需要,可以尝试获取结果,这将阻塞直到结果可用或发生异常
        try {
            String result = resultFuture.get(); // get() 方法可能会抛出InterruptedException, ExecutionException
            System.out.println("第三方API调用结果: " + result);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭线程池,不再接受新任务,已提交的任务会执行完毕
            executorService.shutdown();
            // 如果需要等待所有任务执行完毕再结束程序,可以使用 executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        }
    }

    /**
     * 模拟调用第三方API的方法
     * @param data 要发送给API的数据
     * @return API响应数据
     */
    private static String callThirdPartyApi(String data) {
        // 这里应该是实际的API调用逻辑,例如使用HttpClient发送HTTP请求
        // 为了示例,我们简单返回一个字符串
        System.out.println("模拟调用第三方API with data: " + data);
        try {
            Thread.sleep(2000); // 模拟API调用耗时
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
        return "API响应数据: " + data.toUpperCase();
    }
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值