扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
线程池简介:
创新互联主要为客户提供服务项目涵盖了网页视觉设计、VI标志设计、营销推广、网站程序开发、HTML5响应式成都网站建设、手机网站制作设计、微商城、网站托管及网站维护、WEB系统开发、域名注册、国内外服务器租用、视频、平面设计、SEO优化排名。设计、前端、后端三个建站步骤的完善服务体系。一人跟踪测试的建站服务标准。已经为效果图设计行业客户提供了网站开发服务。
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。
如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。
一个线程池包括以下四个基本组成部分:
1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。
线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目,看一个例子:
假设一个服务器一天要处理50000个请求,并且每个请求需要一个单独的线程完成。在线程池中,线程数一般是固定的,所以产生线程总数不会超过线程池中线程的数目,而如果服务器不利用线程池来处理这些请求则线程总数为50000。一般线程池大小是远小于50000。所以利用线程池的服务器程序不会为了创建50000而在处理请求时浪费时间,从而提高效率。
代码实现中并没有实现任务接口,而是把Runnable对象加入到线程池管理器(ThreadPool),然后剩下的事情就由线程池管理器(ThreadPool)来完成了
package mine.util.thread;
import java.util.LinkedList;
import java.util.List;
/**
* 线程池类,线程管理器:创建线程,执行任务,销毁线程,获取线程基本信息
*/
public final class ThreadPool {
// 线程池中默认线程的个数为5
private static int worker_num = 5;
// 工作线程
private WorkThread[] workThrads;
// 未处理的任务
private static volatile int finished_task = 0;
// 任务队列,作为一个缓冲,List线程不安全
private ListRunnable taskQueue = new LinkedListRunnable();
private static ThreadPool threadPool;
// 创建具有默认线程个数的线程池
private ThreadPool() {
this(5);
}
// 创建线程池,worker_num为线程池中工作线程的个数
private ThreadPool(int worker_num) {
ThreadPool.worker_num = worker_num;
workThrads = new WorkThread[worker_num];
for (int i = 0; i worker_num; i++) {
workThrads[i] = new WorkThread();
workThrads[i].start();// 开启线程池中的线程
}
}
// 单态模式,获得一个默认线程个数的线程池
public static ThreadPool getThreadPool() {
return getThreadPool(ThreadPool.worker_num);
}
// 单态模式,获得一个指定线程个数的线程池,worker_num(0)为线程池中工作线程的个数
// worker_num=0创建默认的工作线程个数
public static ThreadPool getThreadPool(int worker_num1) {
if (worker_num1 = 0)
worker_num1 = ThreadPool.worker_num;
if (threadPool == null)
threadPool = new ThreadPool(worker_num1);
return threadPool;
}
// 执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定
public void execute(Runnable task) {
synchronized (taskQueue) {
taskQueue.add(task);
taskQueue.notify();
}
}
// 批量执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定
public void execute(Runnable[] task) {
synchronized (taskQueue) {
for (Runnable t : task)
taskQueue.add(t);
taskQueue.notify();
}
}
// 批量执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定
public void execute(ListRunnable task) {
synchronized (taskQueue) {
for (Runnable t : task)
taskQueue.add(t);
taskQueue.notify();
}
}
// 销毁线程池,该方法保证在所有任务都完成的情况下才销毁所有线程,否则等待任务完成才销毁
public void destroy() {
while (!taskQueue.isEmpty()) {// 如果还有任务没执行完成,就先睡会吧
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 工作线程停止工作,且置为null
for (int i = 0; i worker_num; i++) {
workThrads[i].stopWorker();
workThrads[i] = null;
}
threadPool=null;
taskQueue.clear();// 清空任务队列
}
// 返回工作线程的个数
public int getWorkThreadNumber() {
return worker_num;
}
// 返回已完成任务的个数,这里的已完成是只出了任务队列的任务个数,可能该任务并没有实际执行完成
public int getFinishedTasknumber() {
return finished_task;
}
// 返回任务队列的长度,即还没处理的任务个数
public int getWaitTasknumber() {
return taskQueue.size();
}
// 覆盖toString方法,返回线程池信息:工作线程个数和已完成任务个数
@Override
public String toString() {
return "WorkThread number:" + worker_num + " finished task number:"
+ finished_task + " wait task number:" + getWaitTasknumber();
}
/**
* 内部类,工作线程
*/
private class WorkThread extends Thread {
// 该工作线程是否有效,用于结束该工作线程
private boolean isRunning = true;
/*
* 关键所在啊,如果任务队列不空,则取出任务执行,若任务队列空,则等待
*/
@Override
public void run() {
Runnable r = null;
while (isRunning) {// 注意,若线程无效则自然结束run方法,该线程就没用了
synchronized (taskQueue) {
while (isRunning taskQueue.isEmpty()) {// 队列为空
try {
taskQueue.wait(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (!taskQueue.isEmpty())
r = taskQueue.remove(0);// 取出任务
}
if (r != null) {
r.run();// 执行任务
}
finished_task++;
r = null;
}
}
// 停止工作,让该线程自然执行完run方法,自然结束
public void stopWorker() {
isRunning = false;
}
}
}
其中Resin从V3.0后需要购买才能用于商业目的,而其他两种则是纯开源的。可以分别从他们的网站上下载最新的二进制包和源代码。
作为Web容器,需要承受较高的访问量,能够同时响应不同用户的请求,能够在恶劣环境下保持较高的稳定性和健壮性。在HTTP服务器领域,ApacheHTTPD的效率是最高的,也是最为稳定的,但它只能处理静态页面的请求,如果需要支持动态页面请求,则必须安装相应的插件,比如mod_perl可以处理Perl脚本,mod_python可以处理Python脚本。
上面介绍的三中Web容器,都是使用Java编写的HTTP服务器,当然他们都可以嵌到Apache中使用,也可以独立使用。分析它们处理客户请求的方法有助于了解Java多线程和线程池的实现方法,为设计强大的多线程服务器打好基础。
Tomcat是使用最广的Java Web容器,功能强大,可扩展性强。最新版本的Tomcat(5.5.17)为了提高响应速度和效率,使用了Apache Portable Runtime(APR)作为最底层,使用了APR中包含Socket、缓冲池等多种技术,性能也提高了。APR也是Apache HTTPD的最底层。可想而知,同属于ASF(Apache Software Foundation)中的成员,互补互用的情况还是很多的,虽然使用了不同的开发语言。
Tomcat 的线程池位于tomcat-util.jar文件中,包含了两种线程池方案。方案一:使用APR的Pool技术,使用了JNI;方案二:使用Java实现的ThreadPool。这里介绍的是第二种。如果想了解APR的Pool技术,可以查看APR的源代码。
ThreadPool默认创建了5个线程,保存在一个200维的线程数组中,创建时就启动了这些线程,当然在没有请求时,它们都处理等待状态(其实就是一个while循环,不停的等待notify)。如果有请求时,空闲线程会被唤醒执行用户的请求。
具体的请求过程是:服务启动时,创建一个一维线程数组(maxThread=200个),并创建空闲线程(minSpareThreads=5个)随时等待用户请求。当有用户请求时,调用 threadpool.runIt(ThreadPoolRunnable)方法,将一个需要执行的实例传给ThreadPool中。其中用户需要执行的实例必须实现ThreadPoolRunnable接口。 ThreadPool首先查找空闲的线程,如果有则用它运行要执行ThreadPoolRunnable;如果没有空闲线程并且没有超过 maxThreads,就一次性创建minSpareThreads个空闲线程;如果已经超过了maxThreads了,就等待空闲线程了。总之,要找到空闲的线程,以便用它执行实例。找到后,将该线程从线程数组中移走。接着唤醒已经找到的空闲线程,用它运行执行实例(ThreadPoolRunnable)。运行完ThreadPoolRunnable后,就将该线程重新放到线程数组中,作为空闲线程供后续使用。
由此可以看出,Tomcat的线程池实现是比较简单的,ThreadPool.java也只有840行代码。用一个一维数组保存空闲的线程,每次以一个较小步伐(5个)创建空闲线程并放到线程池中。使用时从数组中移走空闲的线程,用完后,再归还给线程池。
要想理解清楚java线程池实现原理,明白下面几个问题就可以了:
(1):线程池存在哪些状态,这些状态之间是如何进行切换的呢?
(2):线程池的种类有哪些?
(3):创建线程池需要哪些参数,这些参数的具体含义是什么?
(4):将任务添加到线程池之后运行流程?
(5):线程池是怎么做到重用线程的呢?
(6):线程池的关闭
首先回答第一个问题:线程池存在哪些状态;
查看ThreadPoolExecutor源码便知晓:
[java] view plain copy
// runState is stored in the high-order bits
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;
private static final int TERMINATED = 3 COUNT_BITS;
存在5种状态:
1Running:可以接受新任务,同时也可以处理阻塞队列里面的任务;
2Shutdown:不可以接受新任务,但是可以处理阻塞队列里面的任务;
3Stop:不可以接受新任务,也不处理阻塞队列里面的任务,同时还中断正在处理的任务;
4Tidying:属于过渡阶段,在这个阶段表示所有的任务已经执行结束了,当前线程池中是不存在有效的线程的,并且将要调用terminated方法;
5Terminated:终止状态,这个状态是在调用完terminated方法之后所处的状态;
那么这5种状态之间是如何进行转换的呢?查看ThreadPoolExecutor源码里面的注释便可以知道啦:
[java] view plain copy
* RUNNING - SHUTDOWN
* On invocation of shutdown(), perhaps implicitly in finalize()
* (RUNNING or SHUTDOWN) - STOP
* On invocation of shutdownNow()
* SHUTDOWN - TIDYING
* When both queue and pool are empty
* STOP - TIDYING
* When pool is empty
* TIDYING - TERMINATED
* When the terminated() hook method has completed
从上面可以看到,在调用shutdown方法的时候,线程池状态会从Running转换成Shutdown;在调用shutdownNow方法的时候,线程池状态会从Running/Shutdown转换成Stop;在阻塞队列为空同时线程池为空的情况下,线程池状态会从Shutdown转换成Tidying;在线程池为空的情况下,线程池状态会从Stop转换成Tidying;当调用terminated方法之后,线程池状态会从Tidying转换成Terminate;
在明白了线程池的各个状态以及状态之间是怎么进行切换之后,我们来看看第二个问题,线程池的种类:
(1):CachedThreadPool:缓存线程池,该类线程池中线程的数量是不确定的,理论上可以达到Integer.MAX_VALUE个,这种线程池中的线程都是非核心线程,既然是非核心线程,那么就存在超时淘汰机制了,当里面的某个线程空闲时间超过了设定的超时时间的话,就会回收掉该线程;
(2):FixedThreadPool:固定线程池,这类线程池中是只存在核心线程的,对于核心线程来说,如果我们不设置allowCoreThreadTimeOut属性的话是不存在超时淘汰机制的,这类线程池中的corePoolSize的大小是等于maximumPoolSize大小的,也就是说,如果线程池中的线程都处于活动状态的话,如果有新任务到来,他是不会开辟新的工作线程来处理这些任务的,只能将这些任务放到阻塞队列里面进行等到,直到有核心线程空闲为止;
(3):ScheduledThreadPool:任务线程池,这种线程池中核心线程的数量是固定的,而对于非核心线程的数量是不限制的,同时对于非核心线程是存在超时淘汰机制的,主要适用于执行定时任务或者周期性任务的场景;
(4):SingleThreadPool:单一线程池,线程池里面只有一个线程,同时也不存在非核心线程,感觉像是FixedThreadPool的特殊版本,他主要用于确保任务在同一线程中的顺序执行,有点类似于进行同步吧;
接下来我们来看第三个问题,创建线程池需要哪些参数:
同样查看ThreadPoolExecutor源码,查看创建线程池的构造函数:
[java] view plain copy
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueueRunnable workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
不管你调用的是ThreadPoolExecutor的哪个构造函数,最终都会执行到这个构造函数的,这个构造函数有7个参数,正是由于对这7个参数值的赋值不同,造成生成不同类型的线程池,比如我们常见的CachedThreadPoolExecutor、FixedThreadPoolExecutor
SingleThreadPoolExecutor、ScheduledThreadPoolExecutor,我们老看看这几个参数的具体含义:
1corePoolSize:线程池中核心线程的数量;当提交一个任务到线程池的时候,线程池会创建一个线程来执行执行任务,即使有其他空闲的线程存在,直到线程数达到corePoolSize时不再创建,这时候会把提交的新任务放入到阻塞队列中,如果调用了线程池的preStartAllCoreThreads方法,则会在创建线程池的时候初始化出来核心线程;
2maximumPoolSize:线程池允许创建的最大线程数;如果阻塞队列已经满了,同时已经创建的线程数小于最大线程数的话,那么会创建新的线程来处理阻塞队列中的任务;
3keepAliveTime:线程活动保持时间,指的是工作线程空闲之后继续存活的时间,默认情况下,这个参数只有线程数大于corePoolSize的时候才会起作用,即当线程池中的线程数目大于corePoolSize的时候,如果某一个线程的空闲时间达到keepAliveTime,那么这个线程是会被终止的,直到线程池中的线程数目不大于corePoolSize;如果调用allowCoreThreadTimeOut的话,在线程池中线程数量不大于corePoolSize的时候,keepAliveTime参数也可以起作用的,知道线程数目为0为止;
4unit:参数keepAliveTime的时间单位;
5workQueue:阻塞队列;用于存储等待执行的任务,有四种阻塞队列类型,ArrayBlockingQueue(基于数组的有界阻塞队列)、LinkedBlockingQueue(基于链表结构的阻塞队列)、SynchronousQueue(不存储元素的阻塞队列)、PriorityBlockingQueue(具有优先级的阻塞队列);
6threadFactory:用于创建线程的线程工厂;
7handler:当阻塞队列满了,且没有空闲线程的情况下,也就是说这个时候,线程池中的线程数目已经达到了最大线程数量,处于饱和状态,那么必须采取一种策略来处理新提交的任务,我们可以自己定义处理策略,也可以使用系统已经提供给我们的策略,先来看看系统为我们提供的4种策略,AbortPolicy(直接抛出异常)、CallerRunsPolicy(只有调用者所在的线程来运行任务)、DiscardOldestPolicy(丢弃阻塞队列中最近的一个任务,并执行当前任务)、Discard(直接丢弃);
接下来就是将任务添加到线程池之后的运行流程了;
我们可以调用submit或者execute方法,两者最大的区别在于,调用submit方法的话,我们可以传入一个实现Callable接口的对象,进而能在当前任务执行结束之后通过Future对象获得任务的返回值,submit内部实际上还是执行的execute方法;而调用execute方法的话,是不能获得任务执行结束之后的返回值的;此外,调用submit方法的话是可以抛出异常的,但是调用execute方法的话,异常在其内部得到了消化,也就是说异常在其内部得到了处理,不会向外传递的;
因为submit方法最终也是会执行execute方法的,因此我们只需要了解execute方法就可以了:
在execute方法内部会分三种情况来进行处理:
1:首先判断当前线程池中的线程数量是否小于corePoolSize,如果小于的话,则直接通过addWorker方法创建一个新的Worker对象来执行我们当前的任务;
2:如果说当前线程池中的线程数量大于corePoolSize的话,那么会尝试将当前任务添加到阻塞队列中,然后第二次检查线程池的状态,如果线程池不在Running状态的话,会将刚刚添加到阻塞队列中的任务移出,同时拒绝当前任务请求;如果第二次检查发现当前线程池处于Running状态的话,那么会查看当前线程池中的工作线程数量是否为0,如果为0的话,就会通过addWorker方法创建一个Worker对象出来处理阻塞队列中的任务;
3:如果原先线程池就不处于Running状态或者我们刚刚将当前任务添加到阻塞队列的时候出现错误的话,那么会去尝试通过addWorker创建新的Worker来处理当前任务,如果添加失败的话,则拒绝当前任务请求;
可以看到在上面的execute方法中,我们仅仅只是检查了当前线程池中的线程数量有没有超过corePoolSize的情况,那么当前线程池中的线程数量有没有超过maximumPoolSize是在哪里检测的呢?实际上是在addWorker方法里面了,我们可以看下addWorker里面的一段代码:
[java] view plain copy
if (wc = CAPACITY ||
wc = (core ? corePoolSize : maximumPoolSize))
return false;
如果当前线程数量超过maximumPoolSize的话,直接就会调用return方法,返回false;
其实到这里我们很明显可以知道,一个线程池中线程的数量实际上就是这个线程池中Worker的数量,如果Worker的大小超过了corePoolSize,那么任务都在阻塞队列里面了,Worker是Java对我们任务的一个封装类,他的声明是酱紫的:
[java] view plain copy
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
可以看到他实现了Runnable接口,他是在addWorker方法里面通过new Worker(firstTask)创建的,我们来看看他的构造函数就知道了:
[java] view plain copy
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
而这里的firstTask其实就是我们调用execute或者submit的时候传入的那个参数罢了,一般来说这些参数是实现Callable或者Runnable接口的;
在通过addWorker方法创建出来Worker对象之后,这个方法的最后会执行Worker内部thread属性的start方法,而这个thread属性实际上就是封装了Worker的Thread,执行他的start方法实际上执行的是Worker的run方法,因为Worker是实现了Runnable接口的,在run方法里面就会执行runWorker方法,而runWorker方法里面首先会判断当前我们传入的任务是否为空,不为空的话直接就会执行我们通过execute或者submit方法提交的任务啦,注意一点就是我们虽然会通过submit方法提交实现了Callable接口的对象,但是在调用submit方法的时候,其实是会将Callable对象封装成实现了Runnable接口对象的,不信我们看看submit方法源码是怎么实现的:
[java] view plain copy
public T FutureT submit(CallableT task) {
if (task == null) throw new NullPointerException();
RunnableFutureT ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
看到没有呢,实际上在你传入实现了Callable接口对象的时候,在submit方法里面是会将其封装成RunnableFuture对象的,而RunnableFuture接口是继承了Runnable接口的;那么说白了其实就是直接执行我们提交任务的run方法了;如果为空的话,则会通过getTask方法从阻塞队列里面拿出一个任务去执行;在任务执行结束之后继续从阻塞队列里面拿任务,直到getTask的返回值为空则退出runWorker内部循环,那么什么情况下getTask返回为空呢?查看getTask方法的源码注释可以知道:在Worker必须需要退出的情况下getTask会返回空,具体什么情况下Worker会退出呢?(1):当Worker的数量超过maximumPoolSize的时候;(2):当线程池状态为Stop的时候;(3):当线程池状态为Shutdown并且阻塞队列为空的时候;(4):使用等待超时时间从阻塞队列中拿数据,但是超时之后仍然没有拿到数据;
如果runWorker方法退出了它里面的循环,那么就说明当前阻塞队列里面是没有任务可以执行的了,你可以看到在runWorker方法内部的finally语句块中执行了processWorkerExit方法,用来对Worker对象进行回收操作,这个方法会传入一个参数表示需要删除的Worker对象;在进行Worker回收的时候会调用tryTerminate方法来尝试关闭线程池,在tryTerminate方法里面会检查是否有Worker在工作,检查线程池的状态,没问题的话就会将当前线程池的状态过渡到Tidying,之后调用terminated方法,将线程池状态更新到Terminated;
从上面的分析中,我们可以看出线程池运行的4个阶段:
(1):poolSize corePoolSize,则直接创建新的线程(核心线程)来执行当前提交的任务;
(2):poolSize = corePoolSize,并且此时阻塞队列没有满,那么会将当前任务添加到阻塞队列中,如果此时存在工作线程(非核心线程)的话,那么会由工作线程来处理该阻塞队列中的任务,如果此时工作线程数量为0的话,那么会创建一个工作线程(非核心线程)出来;
(3):poolSize = corePoolSize,并且此时阻塞队列已经满了,那么会直接创建新的工作线程(非核心线程)来处理阻塞队列中的任务;
(4):poolSize = maximumPoolSize,并且此时阻塞队列也满了的话,那么会触发拒绝机制,具体决绝策略采用的是什么就要看我们创建ThreadPoolExecutor的时候传入的RejectExecutionHandler参数了;
接下来就是线程池是怎么做到重用线程的呢?
个人认为线程池里面重用线程的工作是在getTask里面实现的,在getTask里面是存在两个for死循环嵌套的,他会不断的从阻塞对列里面取出需要执行的任务,返回给我们的runWorker方法里面,而在runWorker方法里面只要getTask返回的任务不是空就会执行该任务的run方法来处理它,这样一直执行下去,直到getTask返回空为止,此时的情况就是阻塞队列里面没有任务了,这样一个线程处理完一个任务之后接着再处理阻塞队列中的另一个任务,当然在线程池中的不同线程是可以并发处理阻塞队列中的任务的,最后在阻塞队列内部不存在任务的时候会去判断是否需要回收Worker对象,其实Worker对象的个数就是线程池中线程的个数,至于什么情况才需要回收,上面已经说了,就是四种情况了;
最后就是线程池是怎样被关闭的呢?
涉及到线程池的关闭,需要用到两个方法,shutdown和shutdownNow,他们都是位于ThreadPoolExecutor里面的,对于shutdown的话,他会将线程池状态切换成Shutdown,此时是不会影响对阻塞队列中任务执行的,但是会拒绝执行新加进来的任务,同时会回收闲置的Worker;而shutdownNow方法会将线程池状态切换成Stop,此时既不会再去处理阻塞队列里面的任务,也不会去处理新加进来的任务,同时会回收所有Worker;
线程池通俗的描述就是预先创建若干空闲线程 等到需要用多线程去处理事务的时候去唤醒某些空闲线程执行处理任务 这样就省去了频繁创建线程的时间 因为频 繁创建线程是要耗费大量的CPU资源的 如果一个应用程序需要频繁地处理大量并发事务 不断的创建销毁线程往往会大大地降低系统的效率 这时候线程池就派 上用场了
本文旨在使用Java语言编写一个通用的线程池 当需要使用线程池处理事务时 只需按照指定规范封装好事务处理对象 然后用已有的线程池对象去自动选择空 闲线程自动调用事务处理对象即可 并实现线程池的动态修改(修改当前线程数 最大线程数等) 下面是实现代码
//ThreadTask java
package polarman threadpool;
/** *//**
*线程任务
* @author ryang
*
*/
public interface ThreadTask {
public void run();
}
//PooledThread java
package polarman threadpool;
import java util Collection; import java util Vector;
/** *//**
*接受线程池管理的线程
* @author ryang
*
*/
public class PooledThread extends Thread {
protected Vector tasks = new Vector();
protected boolean running = false;
protected boolean stopped = false;
protected boolean paused = false;
protected boolean killed = false;
private ThreadPool pool;
public PooledThread(ThreadPool pool) { this pool = pool;
}
public void putTask(ThreadTask task) { tasks add(task);
}
public void putTasks(ThreadTask[] tasks) { for(int i= ; itasks length; i++) this tasks add(tasks[i]);
}
public void putTasks(Collection tasks) { this tasks addAll(tasks);
}
protected ThreadTask popTask() { if(tasks size() ) return (ThreadTask)tasks remove( );
else
return null;
}
public boolean isRunning() {
return running;
}
public void stopTasks() {
stopped = true;
}
public void stopTasksSync() {
stopTasks();
while(isRunning()) { try {
sleep( );
} catch (InterruptedException e) {
}
}
}
public void pauseTasks() {
paused = true;
}
public void pauseTasksSync() {
pauseTasks();
while(isRunning()) { try {
sleep( );
} catch (InterruptedException e) {
}
}
}
public void kill() { if(!running)
interrupt();
else
killed = true;
}
public void killSync() {
kill();
while(isAlive()) { try {
sleep( );
} catch (InterruptedException e) {
}
}
}
public synchronized void startTasks() {
running = true;
this notify();
}
public synchronized void run() { try { while(true) { if(!running || tasks size() == ) { pool notifyForIdleThread(); //System out println(Thread currentThread() getId() + : 空闲 ); this wait(); }else {
ThreadTask task;
while((task = popTask()) != null) { task run(); if(stopped) {
stopped = false;
if(tasks size() ) { tasks clear(); System out println(Thread currentThread() getId() + : Tasks are stopped );
break;
}
}
if(paused) {
paused = false;
if(tasks size() ) { System out println(Thread currentThread() getId() + : Tasks are paused );
break;
}
}
}
running = false;
}
if(killed) {
killed = false;
break;
}
}
}catch(InterruptedException e) {
return;
}
//System out println(Thread currentThread() getId() + : Killed );
}
}
//ThreadPool java
package polarman threadpool;
import java util Collection; import java util Iterator; import java util Vector;
/** *//**
*线程池
* @author ryang
*
*/
public class ThreadPool {
protected int maxPoolSize;
protected int initPoolSize;
protected Vector threads = new Vector();
protected boolean initialized = false;
protected boolean hasIdleThread = false;
public ThreadPool(int maxPoolSize int initPoolSize) { this maxPoolSize = maxPoolSize; this initPoolSize = initPoolSize;
}
public void init() {
initialized = true;
for(int i= ; iinitPoolSize; i++) {
PooledThread thread = new PooledThread(this);
thread start(); threads add(thread);
}
//System out println( 线程池初始化结束 线程数= + threads size() + 最大线程数= + maxPoolSize);
}
public void setMaxPoolSize(int maxPoolSize) { //System out println( 重设最大线程数 最大线程数= + maxPoolSize); this maxPoolSize = maxPoolSize;
if(maxPoolSize getPoolSize())
setPoolSize(maxPoolSize);
}
/** *//**
*重设当前线程数
* 若需杀掉某线程 线程不会立刻杀掉 而会等到线程中的事务处理完成* 但此方法会立刻从线程池中移除该线程 不会等待事务处理结束
* @param size
*/
public void setPoolSize(int size) { if(!initialized) {
initPoolSize = size;
return;
}else if(size getPoolSize()) { for(int i=getPoolSize(); isize imaxPoolSize; i++) {
PooledThread thread = new PooledThread(this);
thread start(); threads add(thread);
}
}else if(size getPoolSize()) { while(getPoolSize() size) { PooledThread th = (PooledThread)threads remove( ); th kill();
}
}
//System out println( 重设线程数 线程数= + threads size());
}
public int getPoolSize() { return threads size();
}
protected void notifyForIdleThread() {
hasIdleThread = true;
}
protected boolean waitForIdleThread() {
hasIdleThread = false;
while(!hasIdleThread getPoolSize() = maxPoolSize) { try { Thread sleep( ); } catch (InterruptedException e) {
return false;
}
}
return true;
}
public synchronized PooledThread getIdleThread() { while(true) { for(Iterator itr=erator(); itr hasNext();) { PooledThread th = (PooledThread)itr next(); if(!th isRunning())
return th;
}
if(getPoolSize() maxPoolSize) {
PooledThread thread = new PooledThread(this);
thread start(); threads add(thread);
return thread;
}
//System out println( 线程池已满 等待 );
if(waitForIdleThread() == false)
return null;
}
}
public void processTask(ThreadTask task) {
PooledThread th = getIdleThread();
if(th != null) { th putTask(task); th startTasks();
}
}
public void processTasksInSingleThread(ThreadTask[] tasks) {
PooledThread th = getIdleThread();
if(th != null) { th putTasks(tasks); th startTasks();
}
}
public void processTasksInSingleThread(Collection tasks) {
PooledThread th = getIdleThread();
if(th != null) { th putTasks(tasks); th startTasks();
}
}
}
下面是线程池的测试程序
//ThreadPoolTest java
import java io BufferedReader; import java io IOException; import java io InputStreamReader;
import polarman threadpool ThreadPool; import polarman threadpool ThreadTask;
public class ThreadPoolTest {
public static void main(String[] args) { System out println( quit 退出 ); System out println( task A 启动任务A 时长为 秒 ); System out println( size 设置当前线程池大小为 ); System out println( max 设置线程池最大线程数为 ); System out println();
final ThreadPool pool = new ThreadPool( ); pool init();
Thread cmdThread = new Thread() { public void run() {
BufferedReader reader = new BufferedReader(new InputStreamReader(System in));
while(true) { try { String line = reader readLine(); String words[] = line split( ); if(words[ ] equalsIgnoreCase( quit )) { System exit( ); }else if(words[ ] equalsIgnoreCase( size ) words length = ) { try { int size = Integer parseInt(words[ ]); pool setPoolSize(size); }catch(Exception e) {
}
}else if(words[ ] equalsIgnoreCase( max ) words length = ) { try { int max = Integer parseInt(words[ ]); pool setMaxPoolSize(max); }catch(Exception e) {
}
}else if(words[ ] equalsIgnoreCase( task ) words length = ) { try { int timelen = Integer parseInt(words[ ]); SimpleTask task = new SimpleTask(words[ ] timelen * ); pool processTask(task); }catch(Exception e) {
}
}
} catch (IOException e) { e printStackTrace();
}
}
}
};
cmdThread start();
/**//*
for(int i= ; i ; i++){
SimpleTask task = new SimpleTask( Task + i (i+ )* ); pool processTask(task);
}*/
}
}
class SimpleTask implements ThreadTask {
private String taskName;
private int timeLen;
public SimpleTask(String taskName int timeLen) { this taskName = taskName; this timeLen = timeLen;
}
public void run() { System out println(Thread currentThread() getId() +
: START TASK + taskName + );
try { Thread sleep(timeLen); } catch (InterruptedException e) {
}
System out println(Thread currentThread() getId() +
: END TASK + taskName + );
}
}
使用此线程池相当简单 下面两行代码初始化线程池
ThreadPool pool = new ThreadPool( ); pool init();
要处理的任务实现ThreadTask 接口即可(如测试代码里的SimpleTask) 这个接口只有一个方法run()
两行代码即可调用
lishixinzhi/Article/program/Java/hx/201311/27203
什么是线程池?
总归为:池化技术 ---》数据库连接池 缓存架构 缓存池 线程池 内存池,连接池,这种思想演变成缓存架构技术--- JDK设计思想有千丝万缕的联系
首先我们从最核心的ThreadPoolExecutor类中的方法讲起,然后再讲述它的实现原理,接着给出了它的使用示例,最后讨论了一下如何合理配置线程池的大小。
Java 中的 ThreadPoolExecutor 类
java.uitl.concurrent.ThreadPoolExecutor 类是线程池中最核心的一个类,因此如果要透彻地了解Java 中的线程池,必须先了解这个类。下面我们来看一下 ThreadPoolExecutor 类的具体实现源码。
在 ThreadPoolExecutor 类中提供了四个构造方法:
从上面的代码可以得知,ThreadPoolExecutor 继承了 AbstractExecutorService 类,并提供了四个构造器,事实上,通过观察每个构造器的源码具体实现,发现前面三个构造器都是调用的第四个构造器进行的初始化工作。
下面解释下一下构造器中各个参数的含义:
corePoolSize:核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads() 或者 prestartCoreThread()方法,从这 2 个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建 corePoolSize 个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到 corePoolSize 后,就会把到达的任务放到缓存队列当中;
maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于 corePoolSize 时,keepAliveTime 才会起作用,直到线程池中的线程数不大于 corePoolSize,即当线程池中的线程数大于 corePoolSize 时,如果一个线程空闲的时间达到 keepAliveTime,则会终止,直到线程池中的线程数不超过 corePoolSize。但是如果调用了 allowCoreThreadTimeOut(boolean) 方法,在线程池中的线程数不大于 corePoolSize 时,keepAliveTime 参数也会起作用,直到线程池中的线程数为0;
unit:参数 keepAliveTime 的时间单位,有 7 种取值,在 TimeUnit 类中有 7 种静态属性:
workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:
ArrayBlockingQueue 和 PriorityBlockingQueue 使用较少,一般使用 LinkedBlockingQueue 和 Synchronous。线程池的排队策略与 BlockingQueue 有关。
threadFactory:线程工厂,主要用来创建线程;
handler:表示当拒绝处理任务时的策略,有以下四种取值:
具体参数的配置与线程池的关系将在下一节讲述。
从上面给出的 ThreadPoolExecutor 类的代码可以知道,ThreadPoolExecutor 继承了AbstractExecutorService,我们来看一下 AbstractExecutorService 的实现:
AbstractExecutorService 是一个抽象类,它实现了 ExecutorService 接口。
我们接着看 ExecutorService 接口的实现:
而 ExecutorService 又是继承了 Executor 接口,我们看一下 Executor 接口的实现:
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流