大家先从ThreadPoolExecutor的总体流程入手:

针对ThreadPoolExecutor代码,我们来看下execute方法:

public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//poolSize大于等于corePoolSize时不增加线程,反之新初始化线程
if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
//线程执行状态外为执行,同时可以添加到队列中
if (runState == RUNNING && workQueue.offer(command)) {
if (runState != RUNNING || poolSize == 0)
ensureQueuedTaskHandled(command);
}
//poolSize大于等于corePoolSize时,新初始化线程
else if (!addIfUnderMaximumPoolSize(command))
//无法添加初始化执行线程,怎么执行reject操作(调用RejectedExecutionHandler)
reject(command); // is shutdown or saturated
}
}

我们再看下真正的线程执行者(Worker):

	private final class Worker implements Runnable {
/**
* Runs a single task between before/after methods.
*/
private void runTask(Runnable task) {
final ReentrantLock runLock = this.runLock;
runLock.lock();
try {
/*
* If pool is stopping ensure thread is interrupted;
* if not, ensure thread is not interrupted. This requires
* a double-check of state in case the interrupt was
* cleared concurrently with a shutdownNow -- if so,
* the interrupt is re-enabled.
*/
//当线程池的执行状态为关闭等,则执行当前线程的interrupt()操作
if ((runState >= STOP ||
(Thread.interrupted() && runState >= STOP)) &&
hasRun)
thread.interrupt();
/*
* Track execution state to ensure that afterExecute
* is called only if task completed or threw
* exception. Otherwise, the caught runtime exception
* will have been thrown by afterExecute itself, in
* which case we don't want to call it again.
*/
boolean ran = false;
beforeExecute(thread, task);
try {
//任务执行
task.run();
ran = true;
afterExecute(task, null);
++completedTasks;
} catch (RuntimeException ex) {
if (!ran)
afterExecute(task, ex);
throw ex;
}
} finally {
runLock.unlock();
}
} /**
* Main run loop
*/
public void run() {
try {
hasRun = true;
Runnable task = firstTask;
firstTask = null;
//判断是否存在需要执行的任务
while (task != null || (task = getTask()) != null) {
runTask(task);
task = null;
}
} finally {
//如果没有,则将工作线程移除,当poolSize为0是则尝试关闭线程池
workerDone(this);
}
}
} /* Utilities for worker thread control */ /**
* Gets the next task for a worker thread to run. The general
* approach is similar to execute() in that worker threads trying
* to get a task to run do so on the basis of prevailing state
* accessed outside of locks. This may cause them to choose the
* "wrong" action, such as trying to exit because no tasks
* appear to be available, or entering a take when the pool is in
* the process of being shut down. These potential problems are
* countered by (1) rechecking pool state (in workerCanExit)
* before giving up, and (2) interrupting other workers upon
* shutdown, so they can recheck state. All other user-based state
* changes (to allowCoreThreadTimeOut etc) are OK even when
* performed asynchronously wrt getTask.
*
* @return the task
*/
Runnable getTask() {
for (;;) {
try {
int state = runState;
if (state > SHUTDOWN)
return null;
Runnable r;
if (state == SHUTDOWN) // Help drain queue
r = workQueue.poll();
//当线程池大于corePoolSize,同时,存在执行超时时间,则等待相应时间,拿出队列中的线程
else if (poolSize > corePoolSize || allowCoreThreadTimeOut)
r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
else
//阻塞等待队列中可以取到新线程
r = workQueue.take();
if (r != null)
return r;
//判断线程池运行状态,如果大于corePoolSize,或者线程队列为空,也或者线程池为终止的工作线程可以销毁
if (workerCanExit()) {
if (runState >= SHUTDOWN) // Wake up others
interruptIdleWorkers();
return null;
}
// Else retry
} catch (InterruptedException ie) {
// On interruption, re-check runState
}
}
} /**
* Performs bookkeeping for an exiting worker thread.
* @param w the worker
*/
//记录执行任务数量,将工作线程移除,当poolSize为0是则尝试关闭线程池
void workerDone(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
if (--poolSize == 0)
tryTerminate();
} finally {
mainLock.unlock();
}
}

通过上述代码,总结下四个关键字的用法

  • corePoolSize 核心线程数量

线程保有量,线程池总永久保存执行线程的数量

  • maximumPoolSize 最大线程数量

最大线程量,线程最多不能超过此属性设置的数量,当大于线程保有量后,会新启动线程来满足线程执行。

  • 线程存活时间

获取队列中任务的超时时间,当阈值时间内无法获取线程,则会销毁处理线程,前提是线程数量在corePoolSize 以上

  • 执行队列

执行队列是针对任务的缓存,任务在提交至线程池时,都会压入到执行队列中。所以这里大家最好设置下队列的上限,防止溢出

ThreadPoolExecuter的几种实现

  public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
  • CachedThreadPool 执行线程不固定,

好处:可以把新增任务全部缓存在一起,

     坏处:只能用在短时间完成的任务(占用时间较长的操作可以导致线程数无限增大,系统资源耗尽)

public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
  • 单线程线程池
       好处:针对单cpu,单线程避免系统资源的抢夺
       坏处:多cpu多线程时,不能完全利用cpu资源
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
  • 固定长度线程池
        好处:线程数量固定,不会存在线程重复初始化
        坏处:没有对队列大小进行限制,线程初始化后,再也不能回收线程资源

ThreadPoolExecutor原理及使用的更多相关文章

  1. Java线程池ThreadPoolExecutor原理和用法

    1.ThreadPoolExecutor构造方法 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAli ...

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

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

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

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

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

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

  5. Java - "JUC线程池" ThreadPoolExecutor原理解析

    Java多线程系列--“JUC线程池”02之 线程池原理(一) ThreadPoolExecutor简介 ThreadPoolExecutor是线程池类.对于线程池,可以通俗的将它理解为"存 ...

  6. Java并发包中线程池ThreadPoolExecutor原理探究

    一.线程池简介 线程池的使用主要是解决两个问题:①当执行大量异步任务的时候线程池能够提供更好的性能,在不使用线程池时候,每当需要执行异步任务的时候直接new一个线程来运行的话,线程的创建和销毁都是需要 ...

  7. 简单看看ThreadPoolExecutor原理

    线程池的作用就不多说了,其实就是解决两类问题:一是当执行大量的异步任务时线程池能够提供较好的性能,在不使用线程池时,每当需要执行异步任务是需要直接new一个线程去执行,而线程的创建和销毁是需要花销的, ...

  8. Java入门系列之线程池ThreadPoolExecutor原理分析思考(十五)

    前言 关于线程池原理分析请参看<http://objcoding.com/2019/04/25/threadpool-running/>,建议对原理不太了解的童鞋先看下此文然后再来看本文, ...

  9. Java 线程池(ThreadPoolExecutor)原理分析与实际运用

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

  10. 线程池 ThreadPoolExecutor 原理及源码笔记

    前言 前面在学习 JUC 源码时,很多代码举例中都使用了线程池 ThreadPoolExecutor,并且在工作中也经常用到线程池,所以现在就一步一步看看,线程池的源码,了解其背后的核心原理. 公众号 ...

随机推荐

  1. 1_使用Java文件的并发写

    为了实现,并发写操作,首先实验一下在本地情况下, 将一个文件切分成若干个 文件块 然后将文件块 通过多线程的并发的方式写入到指定目录下的文件中. 下面是简单的试着实现代码,暂时 先进行记录一下: im ...

  2. MinGW-notepad++开发c/c++程序

    下载MinGW 点击下载 安装好后运行 最后点击左上角的 Installation,开始安装 1.编译: g++ -o a.exe a.cpp gcc -o hello.exe hello.c 2.运 ...

  3. 04_XML_01_入门基础

    [什么是XML] Extensible Markup Language,翻译过来即可扩展标记语言,可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言. 在XML语言中,它允 ...

  4. 滑动条 Trackbar[OpenCV 笔记9]

    OpenCV中没有实现按钮的功能,我们可以利用滑动条来实现按钮功能. , ); trackbarname 轨迹条的名字. winname 窗口的名字,轨迹条会依附在这个窗口上. value 一个指向整 ...

  5. ASP.NET全局文件与防盗链

    添加Web→全局应用程序类,注 文件名不要改 Global.asax 全局文件是对Web应用声明周期的一个事件响应的地方,将Web应用启动时初始化的一些代码写到 Application_Start中, ...

  6. 深入了解float

    1.float的历史   初衷是为了图片的文字环绕,将img设置float 2.破坏性与包裹性  a.父元素没有设置高度,内部元素浮动后,服务元素的高度被破坏了,可以将其父元素设置overflow:h ...

  7. 代码笔记-触摸事件插件hammer.js使用

    如果要使用jquery,则需要下载jquery.hammer.min.js版本 新建一个hammer对象生成的对象是dom对象,不能直接使用jqeury 的  $(this)方法,需要先将其转成jqu ...

  8. [C#]『Barrier』任务并行库使用小计

    Barrier  是一个对象,它可以在并行操作中的所有任务都达到相应的关卡之前,阻止各个任务继续执行. 如果并行操作是分阶段执行的,并且每一阶段要求各任务之间进行同步,则可以使用该对象. --MSDN ...

  9. php中将地址生成迅雷快车旋风链接的代码

    function zhuanhuan() { $urlodd=explode('//',$_GET["url"],2);//把链接分成2段,//前面是第一段,后面的是第二段 $he ...

  10. C# 拷贝数组的几种方式

    突然学到了,所以就放到博客上来共享一下,权当是学习日记吧. 首先说明一下,数组是引用类型的,所以注意不要在复制时复制了地址而没有复制数值哦! 其实在复制数组的时候,一定要用new在堆中开辟一块新的空间 ...