java线程池的工作原理_Java 线程池的介绍以及工作原理
在什么情况下使用线程池?
1.单个任务处理的时间比较短
2.将需处理的任务的数量大
使用线程池的好处:
1. 降低资源消耗: 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
2. 提高响应速度: 当任务到达时,任务可以不需要等到线程创建就能立即执行。
3. 提高线程的可管理性: 线程是稀缺资源,如果无限制的创建。不仅仅会降低系统的稳定性,使用线程池可以统一分配,调优和监控。但是要做到合理的利用线程池。必须对于其实现原理了如指掌。
一个线程池包括以下四个基本组成部分:
1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
在JDK1.6中研究ThreadPoolExecutor类:
volatile intrunState;static final int RUNNING = 0;static final int SHUTDOWN = 1;static final int STOP = 2;static final int TERMINATED = 3;
runState表示当前线程池的状态,它是一个volatile变量用来保证线程之间的可见性;
当创建线程池后,初始时,线程池处于RUNNING状态;
如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;
如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;
当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。
execute方法:
public voidexecute(Runnable command) {if (command == null)throw newNullPointerException();if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {if (runState == RUNNING &&workQueue.offer(command)) {if (runState != RUNNING || poolSize == 0)
ensureQueuedTaskHandled(command);
}else if (!addIfUnderMaximumPoolSize(command))
reject(command);//is shutdown or saturated
}
}
addIfUnderCorePoolSize方法检查如果当前线程池的大小小于配置的核心线程数,说明还可以创建新线程,则启动新的线程执行这个任务。
private booleanaddIfUnderCorePoolSize(Runnable firstTask) {
Thread t= null;final ReentrantLock mainLock = this.mainLock;
mainLock.lock();try{if (poolSize < corePoolSize && runState ==RUNNING)
t=addThread(firstTask);
}finally{
mainLock.unlock();
}return t != null;
}
addThread:
privateThread addThread(Runnable firstTask) {
Worker w= newWorker(firstTask);
Thread t=threadFactory.newThread(w);boolean workerStarted = false;if (t != null) {if (t.isAlive()) //precheck that t is startable
throw newIllegalThreadStateException();
w.thread=t;
workers.add(w);int nt = ++poolSize;if (nt >largestPoolSize)
largestPoolSize=nt;try{
t.start();
workerStarted= true;
}finally{if (!workerStarted)
workers.remove(w);
}
}returnt;
}
Worker,在ThreadPoolExecutor中的内部类
private final class Worker implementsRunnable {/*** The runLock is acquired and released surrounding each task
* execution. It mainly protects against interrupts that are
* intended to cancel the worker thread from instead
* interrupting the task being run.*/
private final ReentrantLock runLock = newReentrantLock();/*** Initial task to run before entering run loop. Possibly null.*/
privateRunnable firstTask;/*** Per thread completed task counter; accumulated
* into completedTaskCount upon termination.*/
volatile longcompletedTasks;/*** Thread this worker is running in. Acts as a final field,
* but cannot be set until thread is created.*/Thread thread;/*** Records that the thread assigned to this worker has actually
* executed our run() method. Such threads are the only ones
* that will be interrupted.*/
volatile boolean hasRun = false;
Worker(Runnable firstTask) {this.firstTask =firstTask;
}booleanisActive() {returnrunLock.isLocked();
}/*** Interrupts thread if not running a task.*/
voidinterruptIfIdle() {final ReentrantLock runLock = this.runLock;if(runLock.tryLock()) {try{if (hasRun && thread !=Thread.currentThread())
thread.interrupt();
}finally{
runLock.unlock();
}
}
}/*** Interrupts thread even if running a task.*/
voidinterruptNow() {if(hasRun)
thread.interrupt();
}/*** Runs a single task between before/after methods.*/
private voidrunTask(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.*/
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);throwex;
}
}finally{
runLock.unlock();
}
}/*** Main run loop*/
public voidrun() {try{
hasRun= true;
Runnable task=firstTask;
firstTask= null;while (task != null || (task = getTask()) != null) {
runTask(task);
task= null;
}
}finally{
workerDone(this);
}
}
}
View Code
ensureQueuedTaskHandled:
判断如果当前状态不是RUNING,则当前任务不加入到任务队列中,判断如果状态是停止,线程数小于允许的最大数,且任务队列还不空,则加入一个新的工作线程到线程池来帮助处理还未处理完的任务。
private voidensureQueuedTaskHandled(Runnable command) {final ReentrantLock mainLock = this.mainLock;
mainLock.lock();boolean reject = false;
Thread t= null;try{int state =runState;if (state != RUNNING &&workQueue.remove(command))
reject= true;else if (state < STOP &&poolSize< Math.max(corePoolSize, 1) &&
!workQueue.isEmpty())
t= addThread(null);
}finally{
mainLock.unlock();
}if(reject)
reject(command);
}
voidreject(Runnable command) {
handler.rejectedExecution(command,this);
}
addIfUnderMaximumPoolSize:
addIfUnderMaximumPoolSize检查如果线程池的大小小于配置的最大线程数,并且任务队列已经满了(就是execute方法试图把当前线程加入任务队列时不成功),
说明现有线程已经不能支持当前的任务了,但线程池还有继续扩充的空间,就可以创建一个新的线程来处理提交的任务。
private booleanaddIfUnderMaximumPoolSize(Runnable firstTask) {
Thread t= null;final ReentrantLock mainLock = this.mainLock;
mainLock.lock();try{if (poolSize < maximumPoolSize && runState ==RUNNING)
t=addThread(firstTask);
}finally{
mainLock.unlock();
}return t != null;
}
整个流程:
1、如果线程池的当前大小还没有达到基本大小(poolSize < corePoolSize),那么就新增加一个线程处理新提交的任务;
2、如果当前大小已经达到了基本大小,就将新提交的任务提交到阻塞队列排队,等候处理workQueue.offer(command);
3、如果队列容量已达上限,并且当前大小poolSize没有达到maximumPoolSize,那么就新增线程来处理任务;
4、如果队列已满,并且当前线程数目也已经达到上限,那么意味着线程池的处理能力已经达到了极限,此时需要拒绝新增加的任务。至于如何拒绝处理新增的任务,取决于线程池的饱和策略RejectedExecutionHandler。
================================================
设置合适的线程池大小:
如果是CPU密集型的任务,那么良好的线程个数是实际CPU处理器的个数的1倍;
如果是I/O密集型的任务,那么良好的线程个数是实际CPU处理器个数的1.5倍到2倍
线程池中线程数量:
View Code
为什么+1,与CPU核数相等,表示满核运行,+1的话表示在CPU上存在竞争,两者的竞争力不一样。稍微高一点负荷是不影响的。
==================================================================================
Java中提供了几个Executors类的静态方法:
public static ExecutorService newFixedThreadPool(intnThreads) {return newThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue());
}public staticExecutorService newSingleThreadExecutor() {return newFinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue()));
}public staticExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue());
}
newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;
newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue;
newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。
任务拒绝策略:
当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
demo:
importjava.util.concurrent.ArrayBlockingQueue;importjava.util.concurrent.ThreadPoolExecutor;importjava.util.concurrent.TimeUnit;public classMain {public static voidmain(String[] args) {
ThreadPoolExecutor executor= new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,new ArrayBlockingQueue(5));for(int i=0;i<15;i++){
MyTask myTask= newMyTask(i);
executor.execute(myTask);
System.out.println("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+executor.getQueue().size()+",已执行玩别的任务数目:"+executor.getCompletedTaskCount());
}
executor.shutdown();
}
}class MyTask implementsRunnable {private inttaskNum;public MyTask(intnum) {this.taskNum =num;
}
@Overridepublic voidrun() {
System.out.println("正在执行task "+taskNum);try{
Thread.currentThread().sleep(0);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("task "+taskNum+"执行完毕");
}
}
线程池中线程数目:1,队列中等待执行的任务数目:0,已执行玩别的任务数目:0线程池中线程数目:2,队列中等待执行的任务数目:0,已执行玩别的任务数目:0线程池中线程数目:3,队列中等待执行的任务数目:0,已执行玩别的任务数目:0正在执行task0线程池中线程数目:4,队列中等待执行的任务数目:0,已执行玩别的任务数目:0正在执行task3正在执行task1task 3执行完毕
task 1执行完毕
线程池中线程数目:5,队列中等待执行的任务数目:0,已执行玩别的任务数目:0task 0执行完毕
正在执行task5线程池中线程数目:5,队列中等待执行的任务数目:1,已执行玩别的任务数目:2线程池中线程数目:5,队列中等待执行的任务数目:1,已执行玩别的任务数目:3线程池中线程数目:5,队列中等待执行的任务数目:2,已执行玩别的任务数目:3线程池中线程数目:5,队列中等待执行的任务数目:3,已执行玩别的任务数目:3线程池中线程数目:5,队列中等待执行的任务数目:4,已执行玩别的任务数目:3线程池中线程数目:5,队列中等待执行的任务数目:5,已执行玩别的任务数目:3task 5执行完毕
正在执行task6task 6执行完毕
正在执行task7task 7执行完毕
正在执行task8task 8执行完毕
正在执行task9task 9执行完毕
正在执行task10task 10执行完毕
线程池中线程数目:6,队列中等待执行的任务数目:0,已执行玩别的任务数目:9线程池中线程数目:6,队列中等待执行的任务数目:1,已执行玩别的任务数目:9线程池中线程数目:6,队列中等待执行的任务数目:2,已执行玩别的任务数目:9线程池中线程数目:6,队列中等待执行的任务数目:3,已执行玩别的任务数目:9正在执行task12正在执行task14正在执行task13task 14执行完毕
task 13执行完毕
task 12执行完毕
正在执行task2task 2执行完毕
正在执行task4task 4执行完毕
正在执行task11task 11执行完毕
View Code
相关文章:

.NET 程序设计实验 含记事本通讯录代码
实验一 .NET 程序设计基本流程 【实验内容】 一、控制台、Windows 应用程序、ASP.NET 程序开发流程 1、熟悉开发平台 2、分别开发控制台、Windows 应用程序、ASP.NET 程序下显示“Hello world!”的应用程序, 掌握新建、基本输入输出、程序流程、程序调试…

复制构造函数(拷贝构造函数)
也许很多C的初学者都知道什么是构造函数,但是对复制构造函数(copy constructor)却还很陌生。对于我来说,在写代码的时候能用得上复制构造函数的机会并不多,不过这并不说明复制构造函数没什么用,其实复制构造…
VMware上实现LVS负载均衡(NAT)
本文LVS的实现方式採用NAT模式。关于NAT的拓扑图请參照我的上一篇文章。本文纯粹实验。NAT在生产环境中不推荐使用。原因是Load Balancereasy成为瓶颈! 1.VMware9上安装CentOS-6.5-x86_64-minimal版 2.安装完毕后将其hostname设置为LVS-master hostname LVS-master …

java se13安装教程_在Linux发行版中安装Java 13/OpenJDK 13的方法
本文介绍在Linux发行版Ubuntu 18.04/16.04、Debian 10/9、CentOS 7/8、Fedora 31/30/29中安装Java 13/OpenJDK 13、Java SE Development Kit 13的方法。在Ubuntu 18.04/16.04、Debian 10/9、CentOS 7/8、Fedora 31/30/29中安装OpenJDK 13访问JDK 13版本页面以下载最新的版本&am…

Java api 入门教程 之 JAVA的IO处理
IO是输入和输出的简称,在实际的使用时,输入和输出是有方向的。就像现实中两个人之间借钱一样,例如A借钱给B,相对于A来说是借出,而相对于B来说则是借入。所以在程序中提到输入和输出时,也需要区分清楚是相对…

如何编辑PDF文件,PDF编辑器如何使用
如何编辑PDF呢?其实大多数人都不知道该如何下手,部分人会选择将PDF文件转换成Word然后进行编辑,其实这种方法比较麻烦,大大拉低了我们的工作效率。如果想要提高工作效率更加快速的编辑PDF文件,就可以选择迅捷PDF编辑器…

hdu 4366 Card Collector (容斥原理)
http://acm.hdu.edu.cn/showproblem.php?pid4336题意:有 n 张卡片 ,每张卡片出现的 概率 是 pi 每包至多有 一张卡片 ,也有可能没有 卡片 。求 需要买多少包 才能集齐 n 张卡片 ,求包数的 期望 。题解 : 容斥原理…

java语言二维数组转置_java实现二维数组转置的方法示例
本文实例讲述了java实现二维数组转置的方法。分享给大家供大家参考,具体如下:这里在文件中创建Test2、Exchange、Out三个类在Exchange类中编写exchange()方法,在方法中创建两个数组arraryA、arraryB,arraryB[j][i]arraryA[i][j]实…

使用Apache cxf 和Spring在Tomcat下发布Webservice指南
转载 http://blog.csdn.net/zhangzhaokun/article/details/4750021 最近学习了如何使用apache cxf和Spring发布webservice,虽然网上的资料很多,但是没有一个文档可以让读者按照操作步骤来实现完整的发布流程,都需要多篇文件杂合在一起&#x…

srcache_nginx redis 构建缓存系统应用一例
为什么80%的码农都做不了架构师?>>> srcache_nginx模块相关参数介绍,可以参见 《memc_nginxsrcache_nginxmemcached构建透明的动态页面缓存》。 redis是一种高效的key-value存储。 下面举一例应用,看配置: upstream r…

mysql 删除 修改密码_Mysql数据库root密码忘记了,如何在不删除Mysql的情况下修改密码...
1.cmd中使用 net stop mysql 命令停掉正在运行的mysql 数据库。2.在本地中复制Mysql数据库的安装路径一直到bin路径下。3.到cmd执行 "pushd 步骤2复制路径" 的命令,就会到Mysql数据库安装的bin路径下。4.紧接著执行 mysqld --skip-grant-tables 命令…

通用权限管理系统组件 (GPM - General Permissions Manager) 权限管理以前我们都是自己开发,可是到下一个系统又不适用,又改,加上人员流动大,管理很混乱...
为什么80%的码农都做不了架构师?>>> 权限管理以前我们都是自己开发,可是到下一个系统又不适用,又改,加上人员流动大,管理很混乱 Ψ吉日嘎拉 采用通用权限管理系统,这些烦恼就少了很多了&#x…

【小白的CFD之旅】16 流程
那天听了小牛师兄关于CFD应用的四种境界的说法后,小白发现自己连第一种境界都算不上,自己对于CFD还只是停留在做了少数几个案例的基础上,可以说是对其一无所知。不过小白不是那种遇到挫折就退缩的人,他决定沿着黄师姐的方法从软件…

XWiki 4.3 正式版发布
XWiki 4.3 正式版发布了,工作空间、扩展管理器、分发向导和 REST API 做了很多改进,改进了翻译和新的体验的 Solr 搜索。 XWiki是一个由Java编写的基于LGPL协议发布的开源wiki和应用平台。它的开发平台特性允许创建协作式Web应用,同时也提供了…

新建异常并处理java_java – 动态创建异常的工厂模式
我创建了Exception xml并动态创建并抛出异常.com.package.CheckedExceptionChecked Exception Messagecom.package.UnCheckedExceptionUnChecked Exception Message我根据异常键使用反射动态创建异常对象.public static void throwException(final String key) throws CheckedE…

React navtive
http://www.linuxidc.com/Linux/2015-09/123239.htm 转载于:https://www.cnblogs.com/chenzhenfj/p/5203685.html

c# Pdf 转换图片
1,引入 dll itextsharp.dll、 PDFView.dll、 把 gsdll32.dll 拷贝在项目 bin目录下 ,注意:它不能 直接引用 直接上代码: 1 /// <summary>2 /// 将PDF 相应的页转换为图片3 /// </summary>4 …

Entity Framework 约定
约定,类似于接口,是一个规范和规则,使用Code First 定义约定来配置模型和规则。在这里约定只是记本规则,我们可以通过Data Annotaion或者Fluent API来进一步配置模型。约定的形式有如下几种: 类型发现约定主键约定关系…

java用构造方法定义book类_JAVA基础学习之路(三)类定义及构造方法
类的定义及使用一,类的定义classBook {//定义一个类intprice;//定义一个属性intnum;public static int getMonney(int price, intnum) {//定义一个方法return price*num;}}public classtest2 {public static voidmain(String args[]) {Book monney newBook();//声明…

Linux下如何查看文档的内容
查看文档内容的命令有:cat tac head nl tail more less odcat命令显示文档的全部内容,当文档较大的时候只显示最后的部分,所以cat命令适合查看内容较少的文档。可加选项-n显示行数(此时空白行也会显示行号)。-b空白行则不显示行号。tac与cat显…

Java中getResourceAsStream的用法
Java中getResourceAsStream的用法 首先,Java中的getResourceAsStream有以下几种:1. Class.getResourceAsStream(String path) : path 不以’/开头时默认是从此类所在的包下取资源,以’/开头则是从 ClassPath根下获取。其只是通过p…

Asp.Net MVC3 简单入门详解过滤器Filter
为什么80%的码农都做不了架构师?>>> 前言 在开发大项目的时候总会有相关的AOP面向切面编程的组件,而MVC(特指:Asp.Net MVC,以下皆同)项目中不想让MVC开发人员去关心和写类似身份验证࿰…

mysql 锁语句_mysql-笔记 事务 锁 语句
Start Transaction 或 begin [work] 开始一个事务,开始一个事务,引起其他未提交的事务提交,引起表锁释放commit 提交事务,永久修改rollback 回滚事务,撤消修改set autocommit 在当前会话状态下 启用或不启用 autocommi…

【收藏】Java多线程/并发编程大合集
(一)、【Java并发编程】并发编程大合集-兰亭风雨 【Java并发编程】实现多线程的两种方法 【Java并发编程】线程的中断 【Java并发编程】正确挂起、恢复、终止线程 【Java并发编程】守护线程和线程阻塞 【Java并发编程】Volatile关键字(上&…

《深入理解Android:Wi-Fi,NFC和GPS》章节连载[节选]--第六章 深入理解wi-Fi Simple Configuration...
为什么80%的码农都做不了架构师?>>> 首先感谢各位兄弟姐妹们的耐心等待。本书预计在4月上市发售。从今天开始,我将在博客中连载此书的一些内容。注意,此处连载的是未经出版社编辑的原始稿件,所以样子会有些非专业。 …

iOS 的本地化使用和创建过程
在使用本地化语言之前,来看看本地化语言文件内容的结构(这里我以Chinese为例):"Cancel""取消";"OK""确定";"Tip""信息提示";"Login Faild""登陆失败…

MySQL中改变相邻学生座位_力扣——换座位(数据库的题
小美是一所中学的信息科技老师,她有一张 seat 座位表,平时用来储存学生名字和与他们相对应的座位 id。其中纵列的 id 是连续递增的小美想改变相邻俩学生的座位。你能不能帮她写一个 SQL query 来输出小美想要的结果呢?示例:------…

AnsiToUtf8 和 Utf8ToAnsi
在服务端数据库的处理当中,涉及中文字符的结构体字段,需要转为Utf8后再存储到表项中。从数据库中取出包含中文字符的字段后,如果需要保存到char *类型的结构体成员中,需要转为Ansi后再保存。从数据库中取出类型数字的字段后&#…

常见面试题:重写strcpy() 函数原型
已知strcpy函数的原型是 char* strcpy(char* strDest,const char* strSrc); 1.不调用库函数,实现strcpy函数 2.解释为什么要返回char*; 1.strcpy的实现代码 char* strcpy(char* strDest,const char* strSrc) { if((strDest NULL) || (strSrc NULL)) //[1] thro…

mongodb 系列 ~ journal日志畅谈
一 简介 我们来聊聊Journal日志二 核心观点 WAL 日志先行策略三 开启journal流程 在开启journal的系统中,写操作从请求到写入磁盘共经历5个步骤,在serverStatus()中已经列出各个步骤消耗的时间。 1 Write to privateView 2 prepLogBuffer …