概述

  与数据库连接管理类似,线程的创建和销毁会耗费较大的开销,使用 “池化技术” 来更好地利用当前线程资源,减少因线程创建和销毁带来的开销,这就是线程池产生的原因。

无限创建线程的不足

  在生产环境中,若没有线程池,则需要采用的是 “为每个任务创建一个线程” 的方法,当出现大量的请求时需要创建大量的线程:

  • 线程生命周期的开销非常高:线程的创建和销毁并不是没有代价的。根据平台的不同,实际的开销也有所不同,但线程的创建过程都需要时间,延迟处理的请求,并且需要 JVM 和操作系统提供一些辅助操作。如果请求的到达率非常高且请求的处理过程是轻量级的,例如大多数服务器应用程序就是这种情况,那么为每个请求创建一个新线程将消耗大量的计算资源。
  • 资源消耗:活跃的线程会消耗系统资源,尤其是内存。如果可运行的线程数量多于可用处理器的数量,那么有些线程将闲置。大量空闲的线程会占用许多内存,给垃圾回收器带来压力,而且大量线程在竞争 CPU 资源时还将产生其他的性能开销。如果你已经拥有足够多的线程使 CPU 保持忙碌状态,那么再创建更多的线程反而会降低性能。
  • 稳定性:在可创建线程的数量上存在一个限制。这个限制值将随着平台的不同而不同,并且受多个因素制约,包括 JVM 的启动参数、Thread 构造函数中请求的栈大小,以及底层操作系统对线程的限制等。如果破坏了这些限制,那么很可能会抛出 OOM 异常,要想从这种错误中恢复过来是非常危险的,更简单的方法是通过构造程序来避免超出这些限制。

  在一定的范围内,增加线程可以提供系统的吞吐率,但如果超出了这个范围,再创建再多的线程只会降低程序的执行速度,并且如果过多地创建一个线程,那么整个应用程序将会崩溃。要想避免这种危险,就应该对应用程序可以创建的线程数量进行限制,并且全面地测试应用程序,从而确保在线程数量达到限制时,程序也也不会耗尽资源。

  “为每个任务分配一个线程” 这种方法的问题在于,它没有限制可创建线程的数量,只限制了远程用户提交 HTTP 请求的速率。与其他的并发危险一样,在原型设计和开发阶段,无限制地创建线程或许还能较好地运行,但在应用程序部署后并处于高负载下运行时,才会有问题不断地暴露出来。因此,某个恶意的用户或者过多的用户,都会使 Web 服务器的负载达到某个阈值,从而使服务器崩溃。如果服务器需要提供高可用性,并且在高负载情况下能平缓地降低性能,那么这将是一个严重的故障。

线程池的执行策略

  1). 在执行(execute)一个任务(command)时,获取当前线程池状态(c),然后获取线程池工作线程的数量(workerCountOf(c)),如果当前线程池的工作线程数量少于定义的核心工作线程的数量(corePoolSize),则使用工厂(threadFactory)创建一个核心工作线程;若核心工作线程已满,则无需再创建核心工作线程,继续下一步。

  2). 这里对线程池状态进行进行双检查(double-check)。首先进行第一次状态检查,获取线程池的状态(ctl),判断其是否处于运行状态(isRunning);

  2.1). 如果线程池处于运行状态(isRunning 方法返回结果为 true),则将任务加入到工作队列中(workQueue,是一个阻塞队列 BlockingQueue);如果成功加入(offer 方法返回 true)到工作队列(workQueue),此时进行第二次状态检查,获取线程池的状态(recheck),判断其是否处于运行状态(isRunning 方法返回结果为 true);

    2.1.1). 如果线程池处于运行状态,则表示该任务可以被执行,此时判断当前工作线程数量是否为空(workerCountOf(recheck) 返回 0),若为空则使用工厂(threadFactory)创建一个非核心工作线程(总的工作线程数量不能大于最大工作线程数量 maximumPoolSize);

    2.1.2). 如果线程池处于非运行状态,则表示该任务不可以被执行,此时需要从工作队列中移除(remove)该任务,若移除任务失败,则需要交给拒绝执行处理器(handler)进行拒绝处理(reject)。

  2.2). 如果线程池处于非运行状态(isRunning 方法返回结果为 false)或加入工作队列(workQueue)失败(offer 方法返回 false),则使用工厂(threadFactory)创建一个非核心工作线程(总的工作线程数量不能大于最大工作线程数量 maximumPoolSize);如果创建失败,则需要交给拒绝执行处理器(handler)进行拒绝处理(reject)。

  JDK 中实现代码如下:

    public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
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) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}

  

B2. Concurrent 线程池(Executor)的更多相关文章

  1. Java并发——线程池Executor框架

    线程池 无限制的创建线程 若采用"为每个任务分配一个线程"的方式会存在一些缺陷,尤其是当需要创建大量线程时: 线程生命周期的开销非常高 资源消耗 稳定性 引入线程池 任务是一组逻辑 ...

  2. 多线程之线程池Executor应用

    JDK1.5之后,提供了自带的线程池,以便我们更好的处理线程并发问题. Executor类给我提供了多个线程池创建的方式: 创建固定的线程池 Executors.newFixedThreadPool( ...

  3. (Java 多线程系列)Java 线程池(Executor)

    线程池简介 线程池是指管理同一组同构工作线程的资源池,线程池是与工作队列(Work Queue)密切相关的,其中在工作队列中保存了所有等待执行的任务.工作线程(Worker Thread)的任务很简单 ...

  4. Java 线程池 Executor 框架

    在Java中,可以通过new Thread 的方法来创建一个新的线程执行任务,但是线程的创建是非常耗时的,而且创建出来的新的线程都各自运行.缺乏统一的管理,这样的后果是可能导致创建过多的线程从而过度消 ...

  5. Java线程池Executor&ThreadPool

    java自1.5版本之后,提供线程池,供开发人员快捷方便的创建自己的多线程任务.下面简单的线程池的方法及说明. 1.Executor 线程池的顶级接口.定义了方法execute(Runnable),该 ...

  6. (转)深入详解Java线程池——Executor框架

    转:https://yq.aliyun.com/articles/633782?utm_content=m_1000015330 在Java中,使用线程来异步执行任务.Java线程的创建与销毁需要一定 ...

  7. Concurrent - 线程池

    原创转载请注明出处:https://www.cnblogs.com/agilestyle/p/11426981.html ThreadPoolExecutor底层方法参数: @param corePo ...

  8. Java线程池 Executor框架概述

    线程池的意义 循环利用线程资源,避免重复创建和销毁线程 线程池的任务是异步执行的,只要提交完成就能快速返回,可以提高应用响应性 Java线程池还有一个很重要的意义:Java线程池就是JDK 5 推出的 ...

  9. Java线程池 / Executor / Callable / Future

    为什么需要线程池?   每次都要new一个thread,开销大,性能差:不能统一管理:功能少(没有定时执行.中断等).   使用线程池的好处是,可重用,可管理.   Executor     4种线程 ...

随机推荐

  1. 并不对劲的p3709:大爷的字符串题

    题目大意 区间众数 题解 莫队 代码 #include<algorithm> #include<cmath> #include<cstdio> #include&l ...

  2. SimpliciTI协议栈

    SimpliciTI组网过程介绍 1.SimpliciTI支持点对点和星形的网络拓扑结构.   下面介绍以AP为中心的SimpliciTI网路协议的星形拓扑结构通信过程 1)当ED节点上电之后就扫描信 ...

  3. Swift4 协议

    创建: 2018/02/27 完成: 2018/02/28 更新: 2018/03/07 增加类采用协议时的注意 补充只有类, 结构体, 枚举型可以采用协议 增加为类定义准备的协议( protocol ...

  4. apicloud运行机制

    1 首先在官网apicloud中,创建项目生成config.xml文件,预设风格. 2 将config.xml文件放在项目跟目录中,Vue项目,必须先打包生产dist文件, 3 本地或者云编译(官方工 ...

  5. 用jdbc连接数据库并简单执行SQL语句

    一:版本一.这种存在一个问题就是每执行一次操作都会创建一次Connection链接和且释放一次链接 1:创建pojo对象(OR映射,一个pojo类对应一张数据库表)   package com.yin ...

  6. ASP.NET MVC 实现伪静态

    1  什么是伪静态? 现在很多门户网站或者各大电商平台的网站的链接最后都是.htm或者.htm结尾,那么他们的网页真的是静态的html吗?拿京东来说,有无数个页面都都Html,在商品每时每刻都可能被更 ...

  7. 16-1 WEB存储基本操作

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  8. windows8.1专业版 关闭ie11总是已停止工作

    该问题通常原因: 1 系统重病毒: 2 系统和安装的软件不兼容导致. 解决方案: 1 杀毒更新至最新进行杀毒,仍未解决,重新安装系统: 2 目前身边人员多数属于该情况: 1 如安装了输入法.迅雷或其它 ...

  9. Java中static修饰符

    public class StaticTest { static int i ; static int m=30; int j ; int k=25; static{ i=10; System.out ...

  10. c#拖拽文件

    在“属性”窗口中,先设置MDI的父窗口的AllowDrop 属性更改为true;2.在父窗口的事件中添加下面两个事件 private void Form1_DragEnter(object sende ...