java线程(6)——线程池(下)
上篇博客java线程(5)——线程池(上)介绍了线程池的基本知识,这篇博客我们介绍一下常用的ThreadPoolExecutor。
定义
类图关系:
ThreadPoolExecutor继承了AbstractExecutorService抽象类,而AbstractExecutorService实现了ExecutorService接口。
下面来看下ThreadPoolExecutor的代码:
public class ThreadPoolExecutor extends AbstractExecutorService {
//核心线程池的大小。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
private volatile int corePoolSize;
//线程池中线程的最大数量
private volatile int maximumPoolSize;
//Timeout时间,没有任务执行时最多保持多久时间之后终止。
private volatile long keepAliveTime;
//线程工厂,所有的线程都是通过他创建的。
private volatile ThreadFactory threadFactory;
......
//给定初始化参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
......
}
Tips:
Volatile关键字,意为“不稳定的,易变的”。它经常用在多线程中的类型修饰符,他的作用是确保某条指令或代码不会被省略,而且每次都是直接读取值,而不是使用备份数据。
线程池状态
线程池的几种状态基本上是级别层层递进的,区分的因素有:
是否接受新任务,
是否允许运行队列中的任务,
是否继续执行正在处理的任务等等。
//线程池能接受任务新任务,并且可以运行队列中的任务
private static final int RUNNING = -1 << COUNT_BITS;
//不再接受新任务,但可继续运行队列中的任务
private static final int SHUTDOWN = 0 << COUNT_BITS;
//任务都不再执行,正在处理的任务要中断
private static final int STOP = 1 << COUNT_BITS;
//终止
private static final int TIDYING = 2 << COUNT_BITS;
//terminated()方法执行结束
private static final int TERMINATED = 3 << COUNT_BITS;
状态之间的转化关系图如下:
Worker类
说到线程池,我们经常会把线程池比作是一个工厂,而把线程比作工人。
在ThreadPoolExecutor类中,还真就有一个Worker类,先看一下他的定义。
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
//正在运行
final Thread thread;
//初始运行的任务,可能为null
Runnable firstTask;
//前一个线程任务
volatile long completedTasks;
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
//检查是否可以添加新的线程到当前的线程池中
private boolean addWorker(Runnable firstTask, boolean core) {
}
//创建线程时失败回滚
private void addWorkerFailed(Worker w) {
}
//移除任务
public boolean remove(Runnable task) {
}
......
}
分析:
Worker继承了AbstractQueuedSynchronizer,他是一个基于FIFO队列,也叫同步器。
Worker类实现了Runnable接口,说明他是一个线程类。此外,内部还提供了新增、移除任务等方法。那么什么时候可以新增什么时候又需要移除呢,就涉及到核心方法execute()了。
execute()
核心代码如下:
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();
}
//如果当前线程池状态为running,且成功加入指定队列
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);
}
分析:
上面的代码由三个大的if块组成,前两个比较容易理解,第三个有点绕。
1、当前活动线程<核心池大小,继续调用addWorker创建线程处理任务。
2、前面说到Running状态可以接受新任务,运行队列中的任务。判断当前线程的状态是否为Running,如果是,并且任务成功加入队列,还需要进行二次检查,防止出现shut down现象。
3、如果当前线程状态不是Running或任务加入队列失败,则跳转到else if中,执行reject()方法。
java线程(6)——线程池(下)的更多相关文章
- java 多线程 4 线程池
系统启动一个新线程的成本是比较高的,因为它涉及到与操作系统的交互.在这种情况下,使用线程池可以很好的提供性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池. 与数据库连接池类似 ...
- (转)java自带线程池和队列详细讲解 - CSDN过天的专栏
一简介 线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的.在jdk1.5之后这一情况有了很大的改观.Jdk1.5之后加入了java.util ...
- Java 四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor
介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行一个异步任务你还只是如下new T ...
- Java四种线程池
Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor 时间:20 ...
- java自带线程池和队列详细讲解
Java线程池使用说明 一简介 线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的.在jdk1.5之后这一情况有了很大的改观.Jdk1.5之后 ...
- java并发:线程池、饱和策略、定制、扩展
一.序言 当我们需要使用线程的时候,我们可以新建一个线程,然后显式调用线程的start()方法,这样实现起来非常简便,但在某些场景下存在缺陷:如果需要同时执行多个任务(即并发的线程数量很多),频繁地创 ...
- java笔记--使用线程池优化多线程编程
使用线程池优化多线程编程 认识线程池 在Java中,所有的对象都是需要通过new操作符来创建的,如果创建大量短生命周期的对象,将会使得整个程序的性能非常的低下.这种时候就需要用到了池的技术,比如数据库 ...
- Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor
1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? Java new Thread(new Runnable() { @Override public void ru ...
- Java多线程之线程池
现在是多核的时代,面向多核的编程很重要,因此基于java的并发和多线程开发非常重要. 线程池是于队列密切相关的,其中队列保存了所有等待执行的任务.工作者线程的任务很简单:从队列中获取一个任务,执行任务 ...
- Java多线程和线程池
转自:http://blog.csdn.net/u013142781/article/details/51387749 1.为什么要使用线程池 在Java中,如果每个请求到达就创建一个新线程,开销是相 ...
随机推荐
- SAP ABAP 日期,时间 相关函数
获的两个日期之间的分钟数 data min TYPE i. CALL FUNCTION 'DELTA_TIME_DAY_HOUR' EXPORTING T1 = ' T2 = ' D1 = ' D2 ...
- tomcat端口占用后的解决办法【亲测有效】
https://www.cnblogs.com/zhangtan/p/5856573.html 检测正在使用的端口 这里就以win7为例进行讲解. 首先打开cmd,打开的方法很简单,在开始菜单中直 ...
- jquery图片滚动jquery.scrlooAnimation.js
;(function ($, window, document, undefined) { var pluginName = "scrollAnimations", /** * T ...
- python pandas库——pivot使用心得
python pandas库——pivot使用心得 2017年12月14日 17:07:06 阅读数:364 最近在做基于python的数据分析工作,引用第三方数据分析库——pandas(versio ...
- Mysqldump自定义导出n条记录
很多时候DBA需要导出部分记录至开发.测试环境,因数据量需求较小,如果原库的记录多,且表数量也多,在用mysqldump命令导出时可以添加一个where参数,自定义导出n条记录,而不必全量导出. 示例 ...
- go执行外部应用
go执行外部应用 最近想将原来用asp.net mvc写的一个公司内部网站改用beego来写,但发现其中有一个功能是,能将加密的sqlite文件进行解密,因为这个解密是不能公开的,但有些同事需要查看这 ...
- HDU暑假多校第八场G-Card Game
一.题意 给出N个卡牌,卡牌的正反两面具有两个数字,取值范围为[1,2*n],给出若干个默认正面向上的卡牌,求最小反转多少张卡牌可以使得,每张卡牌朝上的面上都有一个不同的数字,同时满足最小反转次数的反 ...
- 各种数据库分页语句整理以及Oracle数据库中的ROWNUM和ORDER BY的区别
.oracle数据库分页 select * from (select a.*,rownum rc from 表名 where rownum<=endrow) a where a.rc>=s ...
- 什么是 Cookie
什么是 Cookie? Cookie 是一小段文本信息,伴随着用户请求和页面在 Web 服务器和浏览器之间传递.Cookie 包含每次用户访问站点时 Web 应用程序都可以读取的信息. 例如,如果在用 ...
- elasticsearch 拼音+ik分词,spring data elasticsearch 拼音分词
elasticsearch 自定义分词器 安装拼音分词器.ik分词器 拼音分词器: https://github.com/medcl/elasticsearch-analysis-pinyin/rel ...