`
westlifesz
  • 浏览: 117992 次
社区版块
存档分类
最新评论

一个线程池实例

阅读更多

一个线程池的例子,见原文:

http://javaresearch.org/article/57075.htm


继“多线程程序模型研究”文章发布后,最近又继续研究,推出一个比较复杂但功能比较完善,而且可以直接套用的线程池的实例,希望对使用多线程设计的读者有所帮助。

该实例来源于Apache项目源代码,源程序有800余行,功能比较全面,而且是非常完善的,并且运行于诸多服务器如tomcat上,就是分析起来有点繁琐。如果开发人员直接把这段程序拿来修改后使用到自己的开发项目中,不失为拿来主义的上策。

本文对该源程序进行了修改和简化,对其中核心部分进行分析,然后创建测试类进行测试。读者学习之后可以直接模仿和套用,而不需要花费大量时间自己亲自再去写线程池程序了。

关键的程序有2个:线程池类(ThreadPool)和线程池接口(ThreadPoolRunnable)。

首先看较复杂的线程池类程序,文件名为ThreadPool.java,有200余行,需要读者有耐心,内容如下:
---------------------------------------------------------------------------------------
import java.util.Vector;

public class ThreadPool {
public static final int MAX_THREADS = 100;
public static final int MAX_SPARE_THREADS = 50;
public static final int MIN_SPARE_THREADS = 10;
public static final int WORK_WAIT_TIMEOUT = 60 * 1000;

protected Vector pool;
protected MonitorRunnable monitor;
protected int maxThreads;
protected int minSpareThreads;
protected int maxSpareThreads;
protected int currentThreadCount;
protected int currentThreadsBusy;
protected boolean stopThePool;

public ThreadPool() {
maxThreads = MAX_THREADS;
maxSpareThreads = MAX_SPARE_THREADS;
minSpareThreads = MIN_SPARE_THREADS;
currentThreadCount = 0;
currentThreadsBusy = 0;
stopThePool = false;
}

public synchronized void start() {
adjustLimits();
openThreads(minSpareThreads);
monitor = new MonitorRunnable(this);
}

public void setMaxThreads(int maxThreads) {
this.maxThreads = maxThreads;
}

public int getMaxThreads() {
return maxThreads;
}

public void setMinSpareThreads(int minSpareThreads) {
this.minSpareThreads = minSpareThreads;
}

public int getMinSpareThreads() {
return minSpareThreads;
}

public void setMaxSpareThreads(int maxSpareThreads) {
this.maxSpareThreads = maxSpareThreads;
}

public int getMaxSpareThreads() {
return maxSpareThreads;
}

public void runIt(ThreadPoolRunnable r) {
if (null == r) {
throw new NullPointerException();
}
if (0 == currentThreadCount || stopThePool) {
throw new IllegalStateException();
}
ControlRunnable c = null;
synchronized (this) {
if (currentThreadsBusy == currentThreadCount) {
if (currentThreadCount < maxThreads) {
int toOpen = currentThreadCount + minSpareThreads;
openThreads(toOpen);
} else {
while (currentThreadsBusy == currentThreadCount) {
try {
this.wait();
}catch (InterruptedException e) {
}
if (0 == currentThreadCount || stopThePool) {
throw new IllegalStateException();
}
}
}
}
c = (ControlRunnable) pool.lastElement();
pool.removeElement(c);
currentThreadsBusy++;
}
c.runIt(r);
}

public synchronized void shutdown() {
if (!stopThePool) {
stopThePool = true;
monitor.terminate();
monitor = null;
for (int i = 0; i < (currentThreadCount - currentThreadsBusy); i++) {
try {
((ControlRunnable) (pool.elementAt(i))).terminate();
} catch (Throwable t) {
}
}
currentThreadsBusy = currentThreadCount = 0;
pool = null;
notifyAll();
}
}

protected synchronized void checkSpareControllers() {
if (stopThePool) {
return;
}

if ((currentThreadCount - currentThreadsBusy) > maxSpareThreads) {
int toFree = currentThreadCount - currentThreadsBusy - maxSpareThreads;
for (int i = 0; i < toFree; i++) {
ControlRunnable c = (ControlRunnable) pool.firstElement();
pool.removeElement(c);
c.terminate();
currentThreadCount--;
}
}
}

protected synchronized void returnController(ControlRunnable c) {
if (0 == currentThreadCount || stopThePool) {
c.terminate();
return;
}
currentThreadsBusy--;

pool.addElement(c);
notify();
}

protected synchronized void notifyThreadEnd() {
currentThreadsBusy--;
currentThreadCount--;
notify();
openThreads(minSpareThreads);
}

protected void adjustLimits() {
if (maxThreads <= 0) {
maxThreads = MAX_THREADS;
}
if (maxSpareThreads >= maxThreads) {
maxSpareThreads = maxThreads;
}
if (maxSpareThreads <= 0) {
if (1 == maxThreads) {
maxSpareThreads = 1;
} else {
maxSpareThreads = maxThreads / 2;
}
}
if (minSpareThreads > maxSpareThreads) {
minSpareThreads = maxSpareThreads;
}
if (minSpareThreads <= 0) {
if (1 == maxSpareThreads) {
minSpareThreads = 1;
} else {
minSpareThreads = maxSpareThreads / 2;
}
}
}

protected void openThreads(int toOpen) {
if (toOpen > maxThreads) {
toOpen = maxThreads;
}
if (0 == currentThreadCount) {
pool = new Vector(toOpen);
}
for (int i = currentThreadCount; i < toOpen; i++) {
pool.addElement(new ControlRunnable(this));
}
currentThreadCount = toOpen;
}

class MonitorRunnable implements Runnable {
ThreadPool p;
Thread t;
boolean shouldTerminate;
MonitorRunnable(ThreadPool p) {
shouldTerminate = false;
this.p = p;
t = new Thread(this);
t.start();
}
public void run() {
while (true) {
try {
synchronized (this) {
this.wait(WORK_WAIT_TIMEOUT);
}
if (shouldTerminate) {
break;
}
p.checkSpareControllers();
} catch (Throwable t) {
t.printStackTrace();
}
}
}

public synchronized void terminate() {
shouldTerminate = true;
this.notify();
}
}

class ControlRunnable implements Runnable {
ThreadPool p;
Thread t;
ThreadPoolRunnable toRun;
boolean shouldTerminate;
boolean shouldRun;
boolean noThData;
Object thData[] = null;

ControlRunnable(ThreadPool p) {
toRun = null;
shouldTerminate = false;
shouldRun = false;
this.p = p;
t = new Thread(this);
t.start();
noThData = true;
thData = null;
}

public void run() {
while (true) {
try {
synchronized (this) {
if (!shouldRun && !shouldTerminate) {
this.wait();
}
}
if (shouldTerminate) {
break;
}
try {
if (noThData) {
thData = toRun.getInitData();
noThData = false;
}
if (shouldRun) {
toRun.runIt(thData);
}
} catch (Throwable t) {
System.err.println("ControlRunnable Throwable: ");
t.printStackTrace();
shouldTerminate = true;
shouldRun = false;
p.notifyThreadEnd();
} finally {
if (shouldRun) {
shouldRun = false;
p.returnController(this);
}
}
if (shouldTerminate) {
break;
}
} catch (InterruptedException ie) {
}
}
}

public synchronized void runIt(ThreadPoolRunnable toRun) {
if (toRun == null) {
throw new NullPointerException("No Runnable");
}
this.toRun = toRun;
shouldRun = true;
this.notify();
}

public synchronized void terminate() {
shouldTerminate = true;
this.notify();
}
}
}
---------------------------------------------------------------------------------------
以上程序中,关键的是openThreads方法、runIt方法以及2个内部类:MonitorRunnable和ControlRunnable。

刚开始运行的时候,线程池会往Vector对象里装入minSpareThreads个元素,每个元素都是ControlRunnable线程类,ControlRunnable类在其构造方法中启动线程。如果shouldRun和shouldTerminate都是false的话,线程就等待。如果shouldRun为true,就调用ThreadPoolRunnable的runIt(Object[])方法,该接口的方法就是我们需要在自己的任务类中覆盖的方法。

如果minSpareThreads个线程都处于Busy后,线程池会再创建出minSpareThreads个线程。MonitorRunnable是用来监视线程池运行情况的,其线程间隔60秒(WORK_WAIT_TIMEOUT)调用一次线程池类的checkSpareControllers方法,如果发现(currentThreadCount - currentThreadsBusy) > maxSpareThreads,就会调用ControlRunnable类的terminate方法删除空闲线程,准备删除的线程是否空闲是通过shouldTerminate参数来判断的。

线程池接口ThreadPoolRunnable有2个空方法getInitData和runIt,我们一般自己创建一个任务类实现这个线程池接口就可以了,把具体的任务内容放在任务类的runIt方法中。如果不想用getInitData,就让它返回null值。

线程池接口程序很简单,文件名为ThreadPoolRunnable.java,就几行,内容如下:
---------------------------------------------------------------------------------------------
public interface ThreadPoolRunnable {
public Object[] getInitData();

public void runIt(Object thData[]);

}
---------------------------------------------------------------------------------------------

线程池类和线程池接口都已经说完,下面就举个例子说说怎么使用它们了。

我们的任务还是扫描端口(请参考我的“多线程程序模型研究”),文件名为TestThreadPool.java,内容如下:
---------------------------------------------------------------------------------------------
import java.net.InetAddress;
import java.net.Socket;

public class TestThreadPool {

public static void main(String[] args) {
String host = null; //第一个参数,目标主机。
int beginport = 1; //第二个参数,开始端口。
int endport = 65535; //第三个参数,结束端口。
try{
host = args[0];
beginport = Integer.parseInt(args[1]);
endport = Integer.parseInt(args[2]);
if(beginport <= 0 || endport >= 65536 || beginport > endport){
throw new Exception("Port is illegal");
}
}catch(Exception e){
System.out.println("Usage: java PortScannerSingleThread host beginport endport");
System.exit(0);
}

ThreadPool tp = new ThreadPool();
tp.setMaxThreads(100);
tp.setMaxSpareThreads(50);
tp.setMinSpareThreads(10);
tp.start();

for(int i = beginport; i <= endport; i++){
Task task = new Task(host,i);
tp.runIt(task);
}
}

}

class Task implements ThreadPoolRunnable{
String host;
int port;

Task(String host, int port){
this.host = host;
this.port = port;
}

public Object[] getInitData(){
return null;
}

public void runIt(Object thData[]){
Socket s = null;
try{
s = new Socket(InetAddress.getByName(host),port);
System.out.println("The port " + port + " at " + host + " is open.");
}catch(Exception e){

}finally{
try{
if(s != null) s.close();
}catch(Exception e){
}
}
}
}
-----------------------------------------------------------------------------------------------
在TestThreadPool类的main方法中定义了三个参数,分别是目标主机IP地址,开始端口和结束端口。然后通过new ThreadPool()创建线程池类,并通过setMaxThreads、setMaxSpareThreads和setMinSpareThreads设置线程池的maxThreads、maxSpareThreads和minSpareThreads参数。Task类通过实现ThreadPoolRunnable接口,在runIt中定义了具体内容(创建Socket对象达到扫描端口的目的)。

以上程序都在JDK1.4.2的环境下编译并运行通过,输入 java TestThreadPool 10.1.1.182 1 10000 运行得出如下结果:
The port 25 at 10.1.1.182 is open.
The port 110 at 10.1.1.182 is open.
The port 135 at 10.1.1.182 is open.
...
通过以上的线程池类、线程池接口的分析和介绍,读者可以在理解的基础上,直接把这个线程池类和接口拿来,应用到自己的开发项目中。

以上内容如果有任何问题,请和我联系,我的邮件是:qianh@cntmi.com

版权所有,严禁转载

参考:
1、Apache项目的线程池源程序,链接是http://www.docjar.com/html/api/org/apache/tomcat/util/threads/ThreadPool.java.html
2、Java Network Programming, 3rd Edition By Elliotte Rusty Harold,Publisher : O'Reilly,Pub Date : October 2004
3、Java Thread Programming, by Paul Hyde, Sams Publishing
分享到:
评论
2 楼 sunny.yang 2011-01-01  
能不能再加一个Task的队列来缓存一下。
1 楼 sunny.yang 2011-01-01  
不错!讲的比较清楚,也比较经典的结构!

相关推荐

    java 四种线程池实例

    3. **SingleThreadExecutor**: `Executors.newSingleThreadExecutor()`创建一个只有一个线程的线程池,所有任务都会按照提交的顺序串行执行。这个线程池适用于需要保证执行顺序的场景,比如日志记录或者数据库事务。...

    一个完整的线程池的实例

    2. **`Worker` 类**:这是一个内部类,继承自`Thread`,负责实际的任务执行。 - `runner`:当前`Worker`正在执行的`Runnable`任务。 - `wakeup` 方法:用于唤醒当前处于等待状态的`Worker`,使其开始执行新的任务...

    C++线程池实例

    总结来说,“C++线程池实例”是一个展示了如何在C++环境中,特别是在较旧的编译器如VC6.0下,利用线程池来实现并发任务处理的例子。通过理解和实践这个实例,开发者可以更好地理解线程池的原理和使用,提高软件的...

    spring 线程池实例

    线程池是多线程编程中的一个重要概念,它能够有效地管理并发任务的执行,提高系统的性能和效率。本实例将深入探讨Spring中的线程池实现,包括其工作原理、配置方式以及如何在实际应用中使用。 首先,我们要理解...

    在spring boot中使用java线程池ExecutorService的讲解

    在上面的代码中,我们创建了一个线程池配置类,使用 @Bean 注解创建了一个线程池实例,该实例具有 5 个核心线程,10 个最大线程,闲置线程存活 60 秒,使用 ArrayBlockingQueue 作为任务队列。 总结 在 Spring ...

    VC写的一个线程池的管理实例

    在这个"VC写的一个线程池的管理实例"中,我们可以深入理解线程池的工作原理以及如何在VC环境中构建和管理它。 1. **线程池的基本原理**: 线程池是由一组预创建或按需创建的线程组成的,这些线程等待接收任务并...

    Qt实现线程池开发实例

    本实例将探讨如何使用Qt实现一个线程池,并结合QTcpSocket进行网络通信。 **线程池的概念与作用** 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池预先创建...

    linux线程池c源码

    1. **`thrmgr_new`**:创建一个新的线程池实例。 - 参数: - `int max_threads`:线程池的最大线程数。 - `int idle_timeout`:线程空闲时的超时时间。 - `void (*handler)(void *)`:处理器函数,用于处理队列...

    线程池实例(Linux)

    5. **线程池的实现方式**:在C/C++中,可以通过自定义数据结构来实现线程池,如维护一个线程数组和任务队列,同时实现线程调度算法。Java中,`java.util.concurrent.ThreadPoolExecutor`提供了内置的线程池实现。 6...

    java线程池实例详细讲解

    1. **工作队列(Work Queue)**:线程池内部维护了一个任务队列,用于存储待执行的任务。当新任务到来时,如果线程池中的工作线程数量未达到最大值,那么任务会被立即分配给一个工作线程执行;否则,任务会被放入...

    c++ 多线程线程池 demo

    OEasyPool-1.0可能是第三方库或个人实现的一个线程池实例,用于演示如何在C++中构建这样的机制。 线程池的基本组件包括: 1. 工作线程:这些是预先创建的线程,它们等待来自线程池的任务。 2. 任务队列:所有待...

    一个简单的线程池例子

    线程池是多线程编程中的一个重要概念,它在Windows操作系统以及其他支持多线程操作系统的环境中广泛应用。线程池是一种管理线程资源的有效方式,通过预先创建并维护一定数量的线程来处理任务,而不是每次需要执行新...

    VC简单的线程池使用实例

    1.线程池管理器(ThreadPoolManager):用于创建并管理...网上的c/c++线程池多是linux下的,这个是VC6.0的线程池。其涉及的主要文件有:ThreadPoolManage、IThreadExcute、TemporarilyThread、ThreadManage、RegKeyIni。

    一个简单的c++ 线程池

    它可能会创建一个线程池实例,然后提交一些任务(如计算斐波那契数列、打印消息等)到线程池。最后,等待所有任务完成并关闭线程池。 总结: 这个简单的C++线程池实现展示了如何在Linux环境下高效地管理和调度线程...

    Java线程池实例

    简单实用的java线程池实例代码。包括测试代码和工程文件。

    线程池的原理与实现详解

    - `creat_thread_pool(int min_num, int max_num)`: 创建一个线程池实例,需要指定最小线程数`min_num`和最大线程数`max_num`。最小线程数决定了线程池启动时预创建的线程数量,过大或过小都会影响线程池的性能。 #...

    JAVA使用线程池查询大批量数据

    1. **创建线程池**:首先,我们需要创建一个线程池实例。通常,我们可以根据实际需求设置线程池参数。例如,如果数据量大且系统资源充足,可以适当增加线程数量;反之,若系统资源有限,应控制线程数量,避免资源...

    Windows下一个比较完美的线程池实现和示例

    Windows下一个比较完美的线程池实现和示例 本线程池提供了如下功能: 1.能根据任务个数和当前线程的多少在最小/最大线程个数之间自动调整(Vista后的系统有 SetThreadpoolThreadMaximum 等函数有类似功能); 2.能方便...

    java线程池实例

    在实际开发中,Java还提供了一些预定义的线程池,如`Executors.newFixedThreadPool(int nThreads)`创建固定大小的线程池,`Executors.newSingleThreadExecutor()`创建只有一个线程的线程池等。这些预定义的线程池...

    delphi线程池实例

    当你创建一个新的TTask实例并调用其Start方法时,任务会被添加到线程池的任务队列中。线程池会检查当前是否有空闲线程,如果有,就立即执行任务;如果没有,任务会被挂起,直到有线程完成其他任务并变得可用。...

Global site tag (gtag.js) - Google Analytics