带着问题

阿里Java代码规范为什么不允许使用Executors快速创建线程池?

下面的代码输出是什么?

ThreadPoolExecutor executor = new ThreadPoolExecutor(

1, //corePoolSize

100, //maximumPoolSize

100, //keepAliveTime

TimeUnit.SECONDS, //unit

new LinkedBlockingDeque<>(100));//workQueue

for (int i = 0; i < 5; i++) {

final int taskIndex = i;

executor.execute(() -> {

System.out.println(taskIndex);

try {

Thread.sleep(Long.MAX_VALUE);

} catch (InterruptedException e) {

e.printStackTrace();

}

});

}

A) 0 1 2 3 4 5

B) 0~5 顺序不一致输出5行

C) 0

基础

什么是线程池?

线程池可以通过池看出来是一个资源集,任何池的作用都大同小异,主要是用来减少资源创建、初始化的系统开销。

创建线程很“贵”吗?

是的。创建线程的代价是昂贵的。

我们都知道系统中的每个进程有自己独立的内存空间,而被称为轻量级进程的线程也是需要的。

在JVM中默认一个线程需要使用256k~1M(取决于32位还是64位操作系统)的内存。(具体的数组我们不深究,因为随着JVM版本的变化这个默认值随时可能发生变更,我们只需要知道线程是需要占用内存的)

除了内存还有更多吗?

许多文章会将上下文切换、CPU调度列入其中,这边不将线程调度列入是因为睡眠中的线程不会被调度(OS控制),如果不是睡眠中的线程那么是一定需要被调度的。

但在JVM中除了创建时的内存消耗,还会给GC带来压力,如果频繁创建线程那么相对的GC的时候也需要回收对应的线程。

线程池的机制?

可以看到线程池是一种重复利用线程的技术,线程池的主要机制就是保留一定的线程数在没有事情做的时候使之睡眠,当有活干的时候拿一个线程去运行。

这些牵扯到线程池实现的具体策略。

还有哪些常见的池?

线程池

连接池(数据库连接、TCP连接等)

BufferPool

......

Java中的线程池

UML图(Java 8)

可以看到真正的实现类有

ThreadPoolExecutor (1.5)

ForkJoinPool (1.7)

ScheduledThreadPoolExecutor (1.5)

今天我们主要谈谈 ThreadPoolExecutor 也是使用率较高的一个实现。

Executors提供的工厂方法

newCachedThreadPool (ThreadPoolExecutor)

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

newFixedThreadPool (ThreadPoolExecutor)

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

newSingleThreadExecutor (ThreadPoolExecutor)

创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

newScheduledThreadPool (ScheduledThreadPoolExecutor)

创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

newSingleThreadScheduledExecutor (ScheduledThreadPoolExecutor)

创建一个单线程用于定时以及周期性执行任务的需求。

newWorkStealingPool (1.8 ForkJoinPool)

创建一个工作窃取

可以看到各种不同的工厂方法中使用的线程池实现类最终只有3个,对应关系如下:

工厂方法

实现类

newCachedThreadPool

ThreadPoolExecutor

newFixedThreadPool

ThreadPoolExecutor

newSingleThreadExecutor

ThreadPoolExecutor

newScheduledThreadPool

ScheduledThreadPoolExecutor

newSingleThreadScheduledExecutor

ScheduledThreadPoolExecutor

newWorkStealingPool

ForkJoinPool

ThreadPoolExecutor

首先我们看下 ThreadPoolExecutor 的完全构造函数

public ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler)

corePoolSize

核心池大小,除非设置了 allowCoreThreadTimeOut 否则哪怕线程超过空闲时间,池中也要最少要保留这个数目的线程。

需要注意的是,corePoolSize所需的线程并不是立即创建的,需要在提交任务之后进行创建,所以如果有大量的缓存线程数可以先提交一个空任务让线程池将线程先创建出来,从而提升后续的执行效率。

maximumPoolSize

允许的最大线程数。

keepAliveTime

空闲线程空闲存活时间,核心线程需要 allowCoreThreadTimeOut 为true才会退出。

unit

与 keepAliveTime 配合,设置 keepAliveTime 的单位,如:毫秒、秒。

workQueue

线程池中的任务队列。上面提到线程池的主要作用是复用线程来处理任务,所以我们需要一个队列来存放需要执行的任务,在使用池中的线程来处理这些任务,所以我们需要一个任务队列。

threadFactory

当线程池判断需要新的线程时通过线程工程创建线程。

handler

执行被阻止时的处理程序,线程池无法处理。这个与任务队列相关,比如队列中可以指定队列大小,如果超过了这个大小该怎么办呢?JDK已经为我们考虑到了,并提供了4个默认实现。

下列是JDK中默认携带的策略:

AbortPolicy (默认)

抛出 RejectedExecutionException 异常。

CallerRunsPolicy

调用当前线程池所在的线程去执行。

DiscardPolicy

直接丢弃当前任务。

DiscardOldestPolicy

将最旧的任务丢弃,将当前任务添加到队列。

容易混淆的参数:corePoolSize maximumPoolSize workQueue

任务队列、核心线程数、最大线程数的逻辑关系

当线程数小于核心线程数时,创建线程。

当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。

当线程数大于等于核心线程数,且任务队列已满

若线程数小于最大线程数,创建线程

若线程数等于最大线程数,调用拒绝执行处理程序(默认效果为:抛出异常,拒绝任务)

那么这三个参数推荐如何设置,有最优值吗?

由于java对于协程的支持不友好,所以会大量依赖于线程池和线程。

从而这个值没有最优推荐,需要根据业务需求情况来进行设置。

不同的需求类型可以创建多个不同的线程池来执行。

问题1:阿里开发规范为什么不允许Executors快速创建线程池?

可以看到原因很简单

newSingleThreadExecutor

newFixedThreadPool

在 workQueue 参数直接 使用了 new LinkedBlockingQueue() 理论上可以无限添加任务到线程池。

public static ExecutorService newFixedThreadPool(int nThreads) {

return new ThreadPoolExecutor(nThreads, nThreads,

0L, TimeUnit.MILLISECONDS,

new LinkedBlockingQueue();

}

public static ExecutorService newSingleThreadExecutor() {

return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1,

1,

0L,

TimeUnit.MILLISECONDS,

new LinkedBlockingQueue()));

}

如果提交到线程池的任务由问题,比如 sleep 永久,会造成内存泄漏,最终导致OOM。

同时 阿里还推荐自定义 threadFactory 设置线程名称便于以后排查问题。

问题2:下面的代码输出是什么?

应该选C。

虽然最大线程数有100但核心线程数为1,任务队列由100。

满足了 '当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。' 这个条件。

所以后续添加的任务都会被堵塞。

最后

关于 ThreadPoolExecutor 的逻辑在实际使用的时候会有点奇怪,因为线程池中的线程并没有超过最大线程数,有没有一种可能当任务被堵塞很久的时候创建新的线程池来处理呢?

这边推荐大家使用 newWorkStealingPool,也就是ForkJoinPool。采取了工作窃取的模式。

后续会跟大家一起聊聊 ForkJoinPool。

java线程不执行_java线程池,阿里为什么不允许使用Executors?相关推荐

  1. java线程立刻执行_Java 线程调用start()后会立即执行run()方法吗?

    别想当然 问题 Java 线程调用start()后会立即执行run()方法吗? 我们在开发中,经常和线程打交道,有些东西总是司空见惯,想当然地认为某些事情理所当然... 但是今天偶然发现一个有趣的现象 ...

  2. java io密集型任务_Java线程池讲解——针对IO密集型任务

    sap java开发技术详解&mdash基础 94.01元 (需用券) 去购买 > 针对 IO 密集型的任务,我们可以针对原本的线程池做一些改造,从而可以提高任务的处理效率. 基本 在阿 ...

  3. java assert使用场景_Java线程池的四种用法与使用场景

    一.如下方式存在的问题 new Thread() { @Override public void run() { // 业务逻辑 }}.start(); 1.首先频繁的创建.销毁对象是一个很消耗性能的 ...

  4. java睡眠后继续执行_Java线程只能有千个,而Go的Goroutine能有上百万个

    前言 哈喽,大家好,我是asong,我又来做知识分享了. 对于做过Java开发的程序员来说,或许会遇到这个问题:java.lang.OutOfMemoryError: Unable to create ...

  5. java new thread参数_java线程池01-ThreadPoolExecutor构造方法参数的使用规则

    为了更好的使用多线程,JDK提供了线程池供开发人员使用,目的在于减少线程的创建和销毁次数,以此达到线程的重复利用. 其中ThreadPoolExecutor是线程池中最核心的一个类,我们先简单看一下这 ...

  6. java executor 源码_Java线程池ThreadPoolExecutor深度探索及源码解析

    我们的程序里,时常要使用多线程.因此多线程的管理变的尤为重要.ThreadPoolExecutor很好的解决了这一点.本篇文章主要从源码入手,分析ThreadPoolExecutor的原理. 1.标记 ...

  7. java线程同时执行_Java 多线程同时执行

    我们创建三个任务与三个线程,让三个线程启动,同时执行三个任务. 任务类必须实现 Runable 接口,而 Runable 接口只包含一个 run 方法.需要实现 这个方法来告诉系统线程将如何运行. 创 ...

  8. java线程交替执行_Java synchronized线程交替运行实现过程详解

    背景 用两个线程交替输出A-Z和1-26,即一个线程输出A-Z,另一个线程输出1-26 而且是交替形式 线程1输出A--线程二输出1 线程1输出B--线程二输出2 线程1输出C--线程二输出3 以此类 ...

  9. java 线程的函数_Java线程总结

    三.    线程间的通信 1.    线程的几种状态 线程有四种状态,任何一个线程肯定处于这四种状态中的一种: 1)    产生(New):线程对象已经产生,但尚未被启动,所以无法执行.如通过new产 ...

最新文章

  1. JavaScript 的同源策略
  2. 2019-4-25 html学习笔记
  3. Python魔法函数
  4. 在Android中使用FlatBuffers - 简介
  5. Spring MVC_Hello World
  6. 如何用python写个人专属群聊提醒小助手?
  7. 大数据带来的安全隐患有哪些
  8. 【渝粤教育】国家开放大学2018年秋季 2707T种植业基础 参考试题
  9. 2021年道路运输企业主要负责人和安全生产管理人员安全考核题型[安考星]
  10. PostgreSQL - 官方手册、中文手册及Github项目地址
  11. WinCE系统USB功能定制
  12. 多次进行hdfs namenode -format命令时,启动DataNode自动死亡的原因
  13. 因变量 方差膨胀系数_如何理解方差膨胀因子(Variance Inflation Factor,VIF)?
  14. Unity开发手游在Android平台的内存优化
  15. 参数函数 matlab,MATLAB的函数参数
  16. IDEA在同一工作空间,显示多个项目
  17. 现代RnB流行音源 – StudioLinkedVST Pure Modern RnB Kontakt
  18. Qt QPainter基本绘图
  19. Taro 牵手腾讯有数,助力小程序数据化运营
  20. 求最大公约数__gcd(a,b)

热门文章

  1. oracle触发器中保存临时数据,Oracle使用游标触发器存储实操
  2. java 拦截所有路径_Java或Web中解决所有路径问题
  3. mysql load 占位符_Mysql占位符插入
  4. mysql约束与外键_MySQL 外键与约束
  5. BUUCTF--- LFI-------本地文件包含(Local File Include)
  6. ajax datatype_JavaScript学习笔记(二十七) ajax及ajax封装
  7. javascript冷门吗_分享几个html5冷门小知识
  8. Gcd HDU - 6545 (基础数论)
  9. typeScript模块四
  10. Django视图简介