线程属于稀缺资源,对于线程的创建规则,引用《阿里巴巴 Java 手册》中的一条进行说明。

  本篇从源码方面介绍ThreadPoolExecutor对象,并简要解析线程池工作原理。

  首先ThreadPoolExecutor中定义了几个线程池状态常量

// runState is stored in the high-order bits
private static final int RUNNING = - << COUNT_BITS;
private static final int SHUTDOWN = << COUNT_BITS;
private static final int STOP = << COUNT_BITS;
private static final int TIDYING = << COUNT_BITS;
private static final int TERMINATED = << COUNT_BITS;
  • RUNNING是运行状态,线程池可以接收新任务
  • SHUTDOWN是在调用shutdown()方法以后处在的状态。表示不再接收新任务,但队列中的任务可以执行完毕
  • STOP是在调用shutdownNow()方法以后的状态。不再接收新任务,中断正在执行的任务,抛弃队列中的任务
  • TIDYING表示所有任务都执行完毕
  • TERMINATED为中止状态,调用terminated()方法后,尝试更新为此状态

  看一下构造方法的参数

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory, RejectedExecutionHandler handler) {
}
  • corePoolSize是线程池的核心线程数
  • maximumPoolSize是线程池允许的最大线程数
  • keepAliveTime为线程空闲时的存活时间
  • unit是keepAliveTime的单位
  • workQueue是用来保存等待被执行的线程的队列
  • threadFactory,线程工厂,通常使用默认工厂,定义了线程名称生成规则。
  • handler是当最大线程数和队列都满了以后,线程池的处理策略

  这里可以简单看下JDK中已有的4种饱和策略handler:

  • AbortPolicy:默认策略,直接抛出异常,throw new RejectedExecutionException
  • CallerRunsPolicy:如果线程池没有中止,直接用调用者的线程资源执行任务
  • DiscardOldestPolicy:如果线程池没有中止,移除队列第一个任务,再执行当前任务
  • DiscardPolicy:什么也不做,直接抛弃这个任务

  也可以自定义饱和策略,只需实现RejectedExecutionHandler接口。

  

  再看一下JDK中的workQueue队列:

  • ArrayBlockingQueue:基于数组实现
  • LinkedBlockingQueue:基于链表实现,默认容量Integer.MAX_VALUE
  • SynchronousQuene:不存储元素,每个插入操作必须等待另一个线程调用移除操作
  • priorityBlockingQuene:具有优先级的队列

  JDK提供了Executors工厂类。这里面有几种实例化线程池的方法:

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

  指定了线程数,且corePoolSize == maximumPoolSize。

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

  可缓存线程池。最大线程数达到Integer.MAX_VALUE。所以在使用该线程池时,一定要控制并发数,不然会创建很多线程,占用大量资源。

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

  只有一个线程的线程池,可以保证提交任务的顺序执行。

  上面的实例方法都很方便,但是在开发中创建线程池时,最好不好使用Executors类中的初始化方法。见《阿里巴巴 Java 手册》:

  

  

  最后看一下ThreadPoolExecutor执行任务的方法

public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == )
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
  • 判断command是否为空
  • 获取线程状态
  • 计算线程池中的线程数量,如果数量小于corePoolSize,就创建一个新线程执行任务
  • 如果线程池正在运行状态,且写入队列成功。
  • 再次获取线程池状态。判断,如果线程状态变成了非运行状态,就从队列中移除任务,调用reject()方法执行饱和策略handler
  • 如果线程池为空,就创建一个新线程执行任务
  • 如果第4步判断没有通过,尝试建立线程执行任务,若没有成功,就执行饱和策略handler

  以一张简单的流程图描述上面的步骤:

  

  

  

  

线程池ThreadPoolExecutor实现原理的更多相关文章

  1. jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一)

    jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一) 线程池介绍 在日常开发中经常会遇到需要使用其它线程将大量任务异步处理的场景(异步化以及提升系统的吞吐量),而在 ...

  2. 21.线程池ThreadPoolExecutor实现原理

    1. 为什么要使用线程池 在实际使用中,线程是很占用系统资源的,如果对线程管理不善很容易导致系统问题.因此,在大多数并发框架中都会使用线程池来管理线程,使用线程池管理线程主要有如下好处: 降低资源消耗 ...

  3. Java8线程池ThreadPoolExecutor底层原理及其源码解析

    小侃一下 日常开发中, 或许不会直接new线程或线程池, 但这些线程相关的基础或思想是非常重要的, 参考林迪效应; 就算没有直接用到, 可能间接也用到了类似的思想或原理, 例如tomcat, jett ...

  4. 线程池ThreadPoolExecutor工作原理

    前言 工作原理 如果使用过线程池,细心的同学肯定会注意到,new一个线程池,但是如果不往里面提交任何任务的话,main方法执行完之后程序会退出,但是如果向线程池中提交了任务的话,main方法执行完毕之 ...

  5. 线程池ThreadPoolExecutor使用原理

    本文来源于翁舒航的博客,点击即可跳转原文观看!!!(被转载或者拷贝走的内容可能缺失图片.视频等原文的内容) 若网站将链接屏蔽,可直接拷贝原文链接到地址栏跳转观看,原文链接:https://www.cn ...

  6. 从源码角度来分析线程池-ThreadPoolExecutor实现原理

    作为一名Java开发工程师,想必性能问题是不可避免的.通常,在遇到性能瓶颈时第一时间肯定会想到利用缓存来解决问题,然而缓存虽好用,但也并非万能,某些场景依然无法覆盖.比如:需要实时.多次调用第三方AP ...

  7. Java线程池ThreadPoolExecutor使用和分析(三) - 终止线程池原理

    相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...

  8. Java 线程池(ThreadPoolExecutor)原理分析与使用

    在我们的开发中"池"的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 使用线程池的好处 1.降低资源消耗 可以重复利用 ...

  9. Java 线程池(ThreadPoolExecutor)原理解析

    在我们的开发中“池”的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 有关java线程技术文章还可以推荐阅读:<关于java多线程w ...

随机推荐

  1. centos搭建lamp环境参考(根据腾讯云实验室)

    1.安装MYSQL 使用 yum 安装 MySQL: yum install mysql-server -y 安装完成后,启动 MySQL 服务: service mysqld restart 设置 ...

  2. layer 弹出层不能居中

    $("#btnAdd").button("loading"); parent.layer.open({ title: '添加菜单', type: 2, maxm ...

  3. android中使用Application

    在android开发过程中,我们可能存储一些全局的变量,最好在正在app的任何一个activity或者service中都可以访问到,这时我们可以使用application. 我们的一个应用就叫appl ...

  4. RedisTemplate访问Redis数据结构(五)——ZSet

    Redis 有序集合和无序集合一样也是string类型元素的集合,且不允许重复的成员.不同的是每个元素都会关联一个double类型的分数.有序集合的成员是唯一的,但分数(score)却可以重复.red ...

  5. 修改docker默认网段

    一. 修改普通docker run启动的容器的网段 https://blog.51cto.com/13670314/2345518?source=dra https://blog.csdn.net/w ...

  6. docker 在centos上的安装实践

    使用yum安装docker yum -y install docker-io [root@localhost goblin]# yum -y install docker-io Loaded plug ...

  7. kubernetes-helm程序包管理器(二十)

    helm概述 Helm是Kubernetes的包管理器,Helm 让我们能够像 yum 管理 rpm 包那样安装.部署.升级和删除容器化应用. Helm的核心术语: Chart:一个helm程序包,是 ...

  8. Netty 系列之 Netty 高性能之道 高性能的三个主题 Netty使得开发者能够轻松地接受大量打开的套接字 Java 序列化

    Netty系列之Netty高性能之道 https://www.infoq.cn/article/netty-high-performance 李林锋 2014 年 5 月 29 日 话题:性能调优语言 ...

  9. 10.1 ‘The server's host key is not cached in the registry’

    10.1 ‘The server's host key is not cached in the registry’ This error message occurs when PuTTY conn ...

  10. 使用thumbnailator给图片加水印

    引入方式:Maven <dependency> <groupId>net.coobird</groupId> <artifactId>thumbnail ...