前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Java线程】复盘线程池使用及思考

【Java线程】复盘线程池使用及思考

作者头像
沁溪源
发布2021-03-22 10:27:23
6200
发布2021-03-22 10:27:23
举报
文章被收录于专栏:沁溪源

系统开发过程中遇到了线程池的使用,这篇文章主要记录一下线程池使用过程中遇到的问题和思考。

  1. 自定义线程池
  2. 自定义线程池 对于如何自定义线程池以及参数设置,请移步溪源《“打工人”初识线程池及自定义线程池实战》
  • 注入Spring容器中 为什么要放入Spring容器中呢???思考如果不放入容器中,会存在什么问题。—资源浪费。如果不放入容器中,每次执行任务时都会创建线程池,执行完任务再关闭线程池。如果任务请求次数很多,便会创建很多线程池,岂不是造成很大的资源浪费,故将其放入容器中管理。 示例:
代码语言:javascript
复制
	@PostConstruct
    @Bean(name = "myThreadPool")
    private ThreadPoolExecutor createThreadPool() {
        return new ThreadPoolExecutor(5, 10,
                0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(10));
    }
  1. 线程工厂 其实实现线程工厂接口,也是为了定制化自己的线程池,例如:设置线程名字,捕获异常机制。
代码语言:javascript
复制
ThreadFactoryBuilder factoryBuilder = new ThreadFactoryBuilder();
//自定义线程名字、处理异常
 ThreadFactoryBuilder factoryBuilder = new ThreadFactoryBuilder();
        ThreadFactory factory = factoryBuilder.setNameFormat("自定义线程-%d")
                .setUncaughtExceptionHandler((thread, throwable) -> {
                    LOG.error("thread {} exception", thread, throwable.getCause());
                })
                .build();
//其次,将线程工厂传入创建线程池方法中,将上面的方法变成
return new ThreadPoolExecutor(10, 20,
                0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(10), factory);

对于捕获异常,大家需 要思考submit(),execute()方法是否能够生效。

下面看下两种不同任务的提交方式,是如何处理异常。先简单揭晓两者的区别:

execute()对于自定义异常是有效的

submit()对于自定义异常是无效的

  • execute() 捕获异常是生效的,设置UncaughtExceptionHandler可以处理记录线程内未捕获的异常。
  • submit()

看下submit()方法是如何执行任务呢?

代码语言:javascript
复制
public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

 protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

从源码中可以看到submit(runnable)提交任务时,submit方法会将我们的Runnable转换为RunnableFuture对象,这个对象实际上是FutureTask实例,然后将这个FutureTask交给execute方法执行。具体类图如下:

从类图中可以看到FutureTask实现RUnnable接口,故看下FutureTask中run()方法的逻辑

可以明确看到run()方法内部捕获了call()方法的异常,并重新设置异常。

设置UncaughtExceptionHandler 是无效的,因为该函数返回一个Future<T>的对象,如果线程执行过程中有未捕获异常,会被包在Future对象中,不会抛出异常。对返回的Future调用get()方法的时候,在get()方法重新抛出包装之后的ExecutionException。这个异常内部包含线程执行过程抛出的异常。这里的思路是 线程执行的异常,也是返回值的一部分,由获取返回值的时候再次抛出。

  1. 线程池是否需要关闭

理论上任务执行结束以后,记得将我们线程池关闭。如果线程池交给Spring容器管理之后,再次关闭线程池之后会出现什么问题?溪源实践结论是任务线程无法执行,因为线程池关闭无法工作,但如何再次开启线程池,溪源还没有研究到,希望有大佬指点迷津。

那线程池到底要关闭吗?现在开始揭晓。。。

  1. 获取子线程执行结果 先说如果不需要子线程返回执行结果时,可以选择使用execute()或者submit()但是不用使用thread.get()方法,会使主线程阻塞

对于线程池中子线程执行任务,主线程需要依据子线程的值做处理时候

示例:

lambda表达式实现

代码语言:javascript
复制
Future<Boolean> taskResult = threadPool.submit(() -> doAction(xxx));

或者实现Callable接口

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/02/24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档