0

0

Java 线程池详解及创建简单实例

高洛峰

高洛峰

发布时间:2017-02-07 14:44:52

|

1539人浏览过

|

来源于php中文网

原创

java 线程池

最近在改进项目的并发功能,但开发起来磕磕碰碰的。看了好多资料,总算加深了认识。于是打算配合查看源代码,总结并发编程的原理。

准备从用得最多的线程池开始,围绕创建、执行、关闭认识线程池整个生命周期的实现原理。后续再研究原子变量、并发容器、阻塞队列、同步工具、锁等等主题。java.util.concurrent里的并发工具用起来不难,但不能仅仅会用,我们要read the fucking source code,哈哈。顺便说声,我用的JDK是1.8。

Executor框架

Executor是一套线程池管理框架,接口里只有一个方法execute,执行Runnable任务。ExecutorService接口扩展了Executor,添加了线程生命周期的管理,提供任务终止、返回任务结果等方法。AbstractExecutorService实现了ExecutorService,提供例如submit方法的默认实现逻辑。

立即学习Java免费学习笔记(深入)”;

然后到今天的主题ThreadPoolExecutor,继承了AbstractExecutorService,提供线程池的具体实现。

构造方法

下面是ThreadPoolExecutor最普通的构造函数,最多有七个参数。具体代码不贴了,只是一些参数校验和设置的语句。

public ThreadPoolExecutor(int corePoolSize,
               int maximumPoolSize,
               long keepAliveTime,
               TimeUnit unit,
               BlockingQueue workQueue,
               ThreadFactory threadFactory,
               RejectedExecutionHandler handler) {
  }

corePoolSize是线程池的目标大小,即是线程池刚刚创建起来,还没有任务要执行时的大小。maximumPoolSize是线程池的最大上限。keepAliveTime是线程的存活时间,当线程池内的线程数量大于corePoolSize,超出存活时间的空闲线程就会被回收。unit就不用说了,剩下的三个参数看后文的分析。

预设的定制线程池

ThreadPoolExecutor预设了一些已经定制好的线程池,由Executors里的工厂方法创建。下面分析newSingleThreadExecutor、newFixedThreadPool、newCachedThreadPool的创建参数。

newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                   0L, TimeUnit.MILLISECONDS,
                   new LinkedBlockingQueue());
  }

newFixedThreadPool的corePoolSize和maximumPoolSize都设置为传入的固定数量,keepAliveTim设置为0。线程池创建后,线程数量将会固定不变,适合需要线程很稳定的场合。

newSingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
      (new ThreadPoolExecutor(1, 1,
                  0L, TimeUnit.MILLISECONDS,
                  new LinkedBlockingQueue()));
  }

newSingleThreadExecutor是线程数量固定为1的newFixedThreadPool版本,保证池内的任务串行。注意到返回的是FinalizableDelegatedExecutorService,来看看源码:

static class FinalizableDelegatedExecutorService
    extends DelegatedExecutorService {
    FinalizableDelegatedExecutorService(ExecutorService executor) {
      super(executor);
    }
    protected void finalize() {
      super.shutdown();
    }
  }

FinalizableDelegatedExecutorService继承了DelegatedExecutorService,仅仅在gc时增加关闭线程池的操作,再来看看DelegatedExecutorService的源码:

static class DelegatedExecutorService extends AbstractExecutorService {
    private final ExecutorService e;
    DelegatedExecutorService(ExecutorService executor) { e = executor; }
    public void execute(Runnable command) { e.execute(command); }
    public void shutdown() { e.shutdown(); }
    public List shutdownNow() { return e.shutdownNow(); }
    public boolean isShutdown() { return e.isShutdown(); }
    public boolean isTerminated() { return e.isTerminated(); }
    //...
  }

代码很简单,DelegatedExecutorService包装了ExecutorService,使其只暴露出ExecutorService的方法,因此不能再配置线程池的参数。本来,线程池创建的参数是可以调整的,ThreadPoolExecutor提供了set方法。使用newSingleThreadExecutor目的是生成单线程串行的线程池,如果还能配置线程池大小,那就没意思了。

Executors还提供了unconfigurableExecutorService方法,将普通线程池包装成不可配置的线程池。如果不想线程池被不明所以的后人修改,可以调用这个方法。

newCachedThreadPool

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                   60L, TimeUnit.SECONDS,
                   new SynchronousQueue());
  }

newCachedThreadPool生成一个会缓存的线程池,线程数量可以从0到Integer.MAX_VALUE,超时时间为1分钟。线程池用起来的效果是:如果有空闲线程,会复用线程;如果没有空闲线程,会新建线程;如果线程空闲超过1分钟,将会被回收。

newScheduledThreadPool

newScheduledThreadPool将会创建一个可定时执行任务的线程池。这个不打算在本文展开,后续会另开文章细讲。

等待队列

newCachedThreadPool的线程上限几乎等同于无限,但系统资源是有限的,任务的处理速度总有可能比不上任务的提交速度。因此,可以为ThreadPoolExecutor提供一个阻塞队列来保存因线程不足而等待的Runnable任务,这就是BlockingQueue。

JDK为BlockingQueue提供了几种实现方式,常用的有:

Bardeen AI
Bardeen AI

使用AI自动执行人工任务

下载

ArrayBlockingQueue:数组结构的阻塞队列

LinkedBlockingQueue:链表结构的阻塞队列

PriorityBlockingQueue:有优先级的阻塞队列

SynchronousQueue:不会存储元素的阻塞队列

newFixedThreadPool和newSingleThreadExecutor在默认情况下使用一个无界的LinkedBlockingQueue。要注意的是,如果任务一直提交,但线程池又不能及时处理,等待队列将会无限制地加长,系统资源总会有消耗殆尽的一刻。所以,推荐使用有界的等待队列,避免资源耗尽。但解决一个问题,又会带来新问题:队列填满之后,再来新任务,这个时候怎么办?后文会介绍如何处理队列饱和。

newCachedThreadPool使用的SynchronousQueue十分有趣,看名称是个队列,但它却不能存储元素。要将一个任务放进队列,必须有另一个线程去接收这个任务,一个进就有一个出,队列不会存储任何东西。因此,SynchronousQueue是一种移交机制,不能算是队列。newCachedThreadPool生成的是一个没有上限的线程池,理论上提交多少任务都可以,使用SynchronousQueue作为等待队列正合适。

饱和策略

当有界的等待队列满了之后,就需要用到饱和策略去处理,ThreadPoolExecutor的饱和策略通过传入RejectedExecutionHandler来实现。如果没有为构造函数传入,将会使用默认的defaultHandler。

private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
public static class AbortPolicy implements RejectedExecutionHandler {
    public AbortPolicy() { }
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
      throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());
    }
  }

AbortPolicy是默认的实现,直接抛出一个RejectedExecutionException异常,让调用者自己处理。除此之外,还有几种饱和策略,来看一下:

public static class DiscardPolicy implements RejectedExecutionHandler {
   public DiscardPolicy() { }
   public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
   }
 }

DiscardPolicy的rejectedExecution直接是空方法,什么也不干。如果队列满了,后续的任务都抛弃掉。

public static class DiscardOldestPolicy implements RejectedExecutionHandler {
   public DiscardOldestPolicy() { }
   public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
     if (!e.isShutdown()) {
       e.getQueue().poll();
       e.execute(r);
     }
   }
 }

DiscardOldestPolicy会将等待队列里最旧的任务踢走,让新任务得以执行。

public static class CallerRunsPolicy implements RejectedExecutionHandler {
   public CallerRunsPolicy() { }
   public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
     if (!e.isShutdown()) {
       r.run();
     }
   }
 }

最后一种饱和策略是CallerRunsPolicy,它既不抛弃新任务,也不抛弃旧任务,而是直接在当前线程运行这个任务。当前线程一般就是主线程啊,让主线程运行任务,说不定就阻塞了。如果不是想清楚了整套方案,还是少用这种策略为妙。

ThreadFactory

每当线程池需要创建一个新线程,都是通过线程工厂获取。如果不为ThreadPoolExecutor设定一个线程工厂,就会使用默认的defaultThreadFactory:

public static ThreadFactory defaultThreadFactory() {
  return new DefaultThreadFactory();
}
static class DefaultThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;
 
    DefaultThreadFactory() {
      SecurityManager s = System.getSecurityManager();
      group = (s != null) ? s.getThreadGroup() :
                 Thread.currentThread().getThreadGroup();
      namePrefix = "pool-" +
             poolNumber.getAndIncrement() +
            "-thread-";
    }
 
    public Thread newThread(Runnable r) {
      Thread t = new Thread(group, r,
                 namePrefix + threadNumber.getAndIncrement(),
                 0);
      if (t.isDaemon())
        t.setDaemon(false);
      if (t.getPriority() != Thread.NORM_PRIORITY)
        t.setPriority(Thread.NORM_PRIORITY);
      return t;
    }
  }

   

平时打印线程池里线程的name时,会输出形如pool-1-thread-1之类的名称,就是在这里设置的。这个默认的线程工厂,创建的线程是普通的非守护线程,如果需要定制,实现ThreadFactory后传给ThreadPoolExecutor即可。

不看代码不总结不会知道,光是线程池的创建就可以引出很多学问。别看平时创建线程池是一句代码的事,其实ThreadPoolExecutor提供了很灵活的定制方法。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

更多java 线程池详解及创建简单实例相关文章请关注PHP中文网!

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Docker容器化部署与DevOps实践
Docker容器化部署与DevOps实践

本专题面向后端与运维开发者,系统讲解 Docker 容器化技术在实际项目中的应用。内容涵盖 Docker 镜像构建、容器运行机制、Docker Compose 多服务编排,以及在 DevOps 流程中的持续集成与持续部署实践。通过真实场景演示,帮助开发者实现应用的快速部署、环境一致性与运维自动化。

0

2026.02.11

Rust异步编程与Tokio运行时实战
Rust异步编程与Tokio运行时实战

本专题聚焦 Rust 语言的异步编程模型,深入讲解 async/await 机制与 Tokio 运行时的核心原理。内容包括异步任务调度、Future 执行模型、并发安全、网络 IO 编程以及高并发场景下的性能优化。通过实战示例,帮助开发者使用 Rust 构建高性能、低延迟的后端服务与网络应用。

1

2026.02.11

Spring Boot企业级开发与MyBatis Plus实战
Spring Boot企业级开发与MyBatis Plus实战

本专题面向 Java 后端开发者,系统讲解如何基于 Spring Boot 与 MyBatis Plus 构建高效、规范的企业级应用。内容涵盖项目架构设计、数据访问层封装、通用 CRUD 实现、分页与条件查询、代码生成器以及常见性能优化方案。通过完整实战案例,帮助开发者提升后端开发效率,减少重复代码,快速交付稳定可维护的业务系统。

3

2026.02.11

包子漫画网页版入口与全集阅读指南_正版免费漫画快速访问方法
包子漫画网页版入口与全集阅读指南_正版免费漫画快速访问方法

本专题汇总了包子漫画官网和网页版入口,提供最新章节抢先看方法、正版免费阅读指南,以及稳定访问方式,帮助用户快速直达包子漫画页面,无广告畅享全集漫画内容。

137

2026.02.10

MC.JS网页版快速畅玩指南_MC.JS官网在线入口及免安装体验方法
MC.JS网页版快速畅玩指南_MC.JS官网在线入口及免安装体验方法

本专题汇总了MC.JS官网入口和网页版快速畅玩方法,提供免安装访问、不同版本(1.8.8、1.12.8)在线体验指南,以及正版网页端操作说明,帮助玩家轻松进入MC.JS世界,实现即时畅玩与高效体验。

80

2026.02.10

谷歌邮箱网页版登录与注册全指南_Gmail账号快速访问与安全操作教程
谷歌邮箱网页版登录与注册全指南_Gmail账号快速访问与安全操作教程

本专题汇总了谷歌邮箱网页版的最新登录入口和注册方法,详细提供官方账号快速访问方式、网页版操作教程及安全登录技巧,帮助用户轻松管理Gmail邮箱账户,实现高效、安全的邮箱使用体验。

65

2026.02.10

铁路12306订票与退改全攻略_高效购票与座位选取技巧
铁路12306订票与退改全攻略_高效购票与座位选取技巧

本专题全面汇总铁路12306订票、退票、改签及候补订单操作技巧,提供车厢座位分布参考、抢票攻略和高铁安检注意事项,帮助新手用户快速掌握高效购票与退改流程,提高出行效率和体验。

78

2026.02.10

TensorFlow2深度学习模型实战与优化
TensorFlow2深度学习模型实战与优化

本专题面向 AI 与数据科学开发者,系统讲解 TensorFlow 2 框架下深度学习模型的构建、训练、调优与部署。内容包括神经网络基础、卷积神经网络、循环神经网络、优化算法及模型性能提升技巧。通过实战项目演示,帮助开发者掌握从模型设计到上线的完整流程。

1

2026.02.10

Vue3组合式API与组件开发实战
Vue3组合式API与组件开发实战

本专题讲解 Vue 3 组合式 API 的核心概念与应用技巧,深入分析响应式系统、生命周期管理、组件设计与复用策略。通过完整项目案例,指导前端开发者实现高性能、结构清晰的 Vue 应用,提升开发效率与代码可维护性。

13

2026.02.10

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 9.1万人学习

Swoft2.x速学之http api篇课程
Swoft2.x速学之http api篇课程

共16课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号