Java线程池详解及常用方法

简介:

Java线程池详解及常用方法
前言
最近被问到了线程池的相关问题。于是准备开始写一些多线程相关的文章。这篇将介绍一下线程池的基本使用。

Executors
Executors是concurrent包下的一个类,为我们提供了创建线程池的简便方法。
Executors可以创建我们常用的四种线程池:
(1)newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。不设上限,提交的任务将立即执行。
(2)newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
(3)newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
(4)newSingleThreadExecutor 创建一个单线程化的线程池执行任务。

Executors的坏处
正常来说,我们不应该使用这种方式创建线程池,应该使用ThreadPoolExecutor来创建线程池。Executors创建的线程池也是调用的ThreadPoolExcutor的构造函数。通过原来可以看出。

我们也看到了这里面的LinkedBlockingQueue并没有指定队列的大小是一个无界队列,这样可能会造成oom。所以我们要使用ThreadPoolExecutor这种方式。

ThreadPoolExecutor
通过源码看到ThreadPoolExecutor比较全的构造函数如下:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

分别解释一下参数的意义
corePoolSize:线程池长期维持的线程数,即使线程处于Idle状态,也不会回收。
maximumPoolSize:线程数的上限
keepAliveTime:空闲的时间,超过这个空闲时间,线程将被回收
unit:空闲时间的时间单位
workQueue:任务的排队队列,当线程都运行的时候,有空的线程将从队列汇总进行拿取
threadFactroy:当核心线程小于满线程的时候,又需要多加线程,则需要从工厂中获取线程
handler:拒绝策略,当线程过多的时候的策略

线程池针对于任务的执行顺序
首先任务过来之后,看看corePoolSize是否有空闲的,有的话就执行。没有的话,放入任务队列里面。然后任务队列会通知线程工厂,赶紧造几个线程,来执行。当任务超过了最大的线程数,就执行拒绝策略,拒绝执行。

submit方法
线程池建立完毕之后,我们就需要往线程池提交任务。通过线程池的submit方法即可。
submit方法接收两种Runable和Callable。
区别如下:
Runable是实现该接口的run方法,callable是实现接口的call方法。
callable允许使用返回值。
callable允许抛出异常。

提交任务的方式
Future submit(Callable task):这种方式可以拿到返回的结果。
void execute(Runnable command):这种方式拿不到。
Future<?> submit(Runnable task):这种方式可以get,但是永远是null。

blockqueue的限制
我们在创建线程池的时候,如果使用Executors。创建的是无界队列,容易造成oom。所以我们要自己执行queue的大小。

BlockingQueue queue = new ArrayBlockingQueue<>(512)
拒绝策略
当任务队列的queue满了的时候,在提交任务,就要触发拒绝策略。队列中默认的拒绝策略是 AbortPolicy。是直接抛出异常的一种策略。
如果是想实现自定义的策略,可以实现RejectedExecutionHandler 接口。
线程池提供了如下的几种策略供选择。
AbortPolicy:默认策略,抛出RejectedExecutionException
DiscardPolicy:忽略当前提交的任务
DiscardOldestPolicy:丢弃任务队列中最老的任务,给新任务腾出地方
CallerRunsPolicy:由提交任务者执行这个任务

ExecutorService executorService = new ThreadPoolExecutor(2, 2,

            0, TimeUnit.SECONDS, 
            new ArrayBlockingQueue<>(512), 
            new ThreadPoolExecutor.DiscardPolicy());

捕捉异常
如之前所说Callable接口的实现,可以获取到结果和异常。通过返回的Future的get方法即可拿到。

ExecutorService executorService = Executors.newFixedThreadPool(5);
Future

    @Override
    public Object call() throws Exception {
        throw new RuntimeException("exception");// 该异常会在调用Future.get()时传递给调用者
    }
});

try {
Object result = future.get();
} catch (InterruptedException e) {
} catch (ExecutionException e) {
e.printStackTrace();
}
正确构造线程池的方式
int poolSize = Runtime.getRuntime().availableProcessors() * 2;
BlockingQueue queue = new ArrayBlockingQueue<>(512);
RejectedExecutionHandler policy = new ThreadPoolExecutor.DiscardPolicy();
executorService = new ThreadPoolExecutor(poolSize, poolSize,

0, TimeUnit.SECONDS,
        queue,
        policy);

获取单个结果
通过submit提交一个任务后,可以获取到一个future,调用get方法会阻塞并等待执行结果。get(long timeout, TimeUnit unit)可以指定等待的超时时间。

获取多个结果
可以使用循环依次调用,也可以使用ExecutorCompletionService。该类的take方式,会阻塞等待某一任务完成。向CompletionService批量提交任务后,只需调用相同次数的CompletionService.take()方法,就能获取所有任务的执行结果,获取顺序是任意的,取决于任务的完成顺序。

void solve(Executor executor, Collection> solvers)
throws InterruptedException, ExecutionException {

CompletionService ecs = new ExecutorCompletionService(executor);// 构造器

for (Callable s : solvers)// 提交所有任务

   ecs.submit(s);
   

int n = solvers.size();
for (int i = 0; i < n; ++i) {// 获取每一个完成的任务

   Result r = ecs.take().get();
   if (r != null)
       use(r);

}
}
这个类是对线程池的一个包装,包装完后,听过他进行submit和take。

单个任务超时
Future.get(long timeout, TimeUnit unit)。方法可以指定等待的超时时间,超时未完成会抛出TimeoutException。

多个任务超时
等待多个任务完成,并设置最大等待时间,可以通过CountDownLatch完成:

public void testLatch(ExecutorService executorService, List tasks)

throws InterruptedException{
  
CountDownLatch latch = new CountDownLatch(tasks.size());
  for(Runnable r : tasks){
      executorService.submit(new Runnable() {
          @Override
          public void run() {
              try{
                  r.run();
              }finally {
                  latch.countDown();// countDown
              }
          }
      });
  }
  latch.await(10, TimeUnit.SECONDS); // 指定超时时间

}
await是总的时间,即使100个任务,需要跑20分钟。我10s超时了 也停止了。

原文地址https://www.cnblogs.com/jichi/p/12560235.html

相关文章
|
消息中间件 存储 分布式计算
|
JavaScript API
本地开发环境请求服务器接口跨域的问题(vue的问题)
本地开发环境请求服务器接口跨域的问题(vue的问题)
784 0
|
5月前
|
人工智能 监控 测试技术
多模型并行场景下的大模型 API 接入设计实践
本文探讨大模型在生产环境中的工程挑战,提出构建统一API接入层,通过解耦业务与模型、多模型协同与容错机制,提升系统稳定性与可维护性,推动AI工程化落地。
|
5月前
|
SQL 人工智能 Java
告别传统 Text-to-SQL:基于 Spring AI Alibaba 的数据分析智能体 DataAgent 深度解析
DataAgent是基于Spring AI Alibaba生态构建的企业级AI数据分析师,融合NL2SQL、多智能体协作与RAG技术,支持多数据源分析、自动纠错与可视化报告生成,让业务人员零代码获取深度数据洞察。
3064 42
告别传统 Text-to-SQL:基于 Spring AI Alibaba 的数据分析智能体 DataAgent 深度解析
|
缓存 监控 搜索推荐
电商生态协同的关键:API接口在电商数据对接中的应用与实践
电商数据对接API接口是连接电商平台与外部系统的智慧桥梁,通过标准化协议实现商品管理、订单处理、支付结算、物流追踪及数据分析等全链路支持。本文从核心功能、对接流程、应用场景和优化策略四个方面解析其技术逻辑与实践路径。API接口助力店铺管理自动化、精准营销与跨境电商全链路管理,同时通过安全防护、性能调优与合规管理提升效能,推动电商行业向智能化、高效化发展。
|
10月前
|
存储 人工智能 分布式计算
程序员的大数据修炼之道01:建立你的大数据知识体系
在AI爆发的2025年,高质量数据成为AI落地的关键。本文带你从零构建企业级大数据平台,详解技术栈、平台构成与数据中台的区别,助力成为AI时代核心人才。
388 0
|
11月前
|
存储 消息中间件 NoSQL
跟着大厂学架构01:如何利用开源方案,复刻B站那套“永不崩溃”的评论系统?
本文基于B站技术团队分享的《B站评论系统的多级存储架构》,解析其在高并发场景下的设计精髓,并通过开源技术栈(MySQL、Redis、Java)复刻其实现。文章深入讲解了多级存储、数据同步、容灾降级等关键设计,并附有完整代码实现,助你掌握大厂架构设计之道。
571 0
|
存储 缓存 分布式计算
Spring-retry 使用指南
该项目为Spring应用程序提供声明式重试支持,它用于Spring Batch、Spring Integration、Apache Hadoop的Spring(以及其他),命令式重试也支持显式使用。
2504 0
Spring-retry 使用指南
|
Java API 开发工具
如何通过淘宝商品详情接口实现商品 SKU、优惠价、价格等参数的实时更新?
要合法获取淘宝商品详情数据,首先需通过淘宝开放平台注册开发者账号并获得App Key与App Secret。接着根据业务需求申请对应的商品详情数据接口权限,并通过官方文档了解接口详情。获取访问令牌后,按照文档构建请求URL并附加必要参数及令牌以调用接口。此外,考虑使用淘宝提供的SDK简化开发流程,如Python SDK等。体验API:b.mrw.so/2Pv6Qu。

热门文章

最新文章