本示例程序由三个类构成第一个是TestThreadPool类它是一个测试程序用来模拟客户端的请求当你运行它时系统首先会显示线程池的初始化信息然后提示你从键盘上输入字符串并按下回车键这时你会发现屏幕上显示信息告诉你某个线程正在处理你的请求如果你快速地输入一行行字符串那么你会发现线程池中不断有线程被唤醒来处理你的请求在本例中我创建了一个拥有个线程的线程池如果线程池中没有可用线程了系统会提示你相应的警告信息但如果你稍等片刻那你会发现屏幕上会陆陆续续提示有线程进入了睡眠状态这时你又可以发送新的请求了
第二个类是ThreadPoolManager类顾名思义它是一个用于管理线程池的类它的主要职责是初始化线程池并为客户端的请求分配不同的线程来进行处理如果线程池满了它会对你发出警告信息
最后一个类是SimpleThread类它是Thread类的一个子类它才真正对客户端的请求进行处理SimpleThread在示例程序初始化时都处于睡眠状态但如果它接受到了ThreadPoolManager类发过来的调度信息则会将自己唤醒并对请求进行处理
首先我们来看一下TestThreadPool类的源码
//TestThreadPooljava
import javaio*;
public class TestThreadPool
{
public static void main(String[] args)
{
try{
BufferedReader br = new BufferedReader(new InputStreamReader(Systemin));
String s;
ThreadPoolManager manager = new ThreadPoolManager();
while((s = brreadLine()) != null)
{
managerprocess(s);
}
}catch(IOException e){}
}
}
由于此测试程序用到了输入输入类因此第行导入了JAVA的基本IO处理包在第行中我们创建了一个名为manager的类它给ThreadPoolManager类的构造函数传递了一个值为的参数告诉ThreadPoolManager类我要一个有个线程的池给我创建一个吧!第行至行是一个无限循环它用来等待用户的键入并将键入的字符串保存在s变量中并调用ThreadPoolManager类的process方法来将这个请求进行处理
下面我们再进一步跟蹤到ThreadPoolManager类中去以下是它的源代码
//ThreadPoolManagerjava
import javautil*;
class ThreadPoolManager
{
private int maxThread;
public Vector vector;
public void setMaxThread(int threadCount)
{
maxThread = threadCount;
}
public ThreadPoolManager(int threadCount)
{
setMaxThread(threadCount);
Systemoutprintln(Starting thread pool);
vector = new Vector();
for(int i = ; i <= ; i++)
{
SimpleThread thread = new SimpleThread(i);
vectoraddElement(thread);
threadstart();
}
}
public void process(String argument)
{
int i;
for(i = ; i <vectorsize(); i++)
{
SimpleThread currentThread = (SimpleThread)vectorelementAt(i);
if(!currentThreadisRunning())
{
Systemoutprintln(Thread + (i+) + is processing: +
argument);
currentThreadsetArgument(argument);
currentThreadsetRunning(true);
return;
}
}
if(i == vectorsize())
{
Systemoutprintln(pool is full try in another time);
}
}
}//end of class ThreadPoolManager
我们先关注一下这个类的构造函数然后再看它的process()方法第-行是它的构造函数首先它给ThreadPoolManager类的成员变量maxThread赋值maxThread表示用于控制线程池中最大线程的数量第行初始化一个数组vector它用来存放所有的SimpleThread类这时候就充分体现了JAVA语言的优越性与艺术性如果你用C语言的话至少要写行以上的代码来完成vector的功能而且C语言数组只能容纳类型统一的基本数据类型无法容纳对象好了闲话少说第-行的循环完成这样一个功能先创建一个新的SimpleThread类然后将它放入vector中去最后用threadstart()来启动这个线程为什么要用start()方法来启动线程呢?因为这是JAVA语言中所规定的如果你不用的话那这些线程将永远得不到激活从而导致本示例程序根本无法运行
下面我们再来看一下process()方法第-行的循环依次从vector数组中选取SimpleThread线程并检查它是否处于激活状态(所谓激活状态是指此线程是否正在处理客户端的请求)如果处于激活状态的话那继续查找vector数组的下一项如果vector数组中所有的线程都处于激活状态的话那它会打印出一条信息提示用户稍候再试相反如果找到了一个睡眠线程的话那第-行会对此进行处理它先告诉客户端是哪一个线程来处理这个请求然后将客户端的请求即字符串argument转发给SimpleThread类的setArgument()方法进行处理并调用SimpleThread类的setRunning()方法来唤醒当前线程来对客户端请求进行处理
可能你还对setRunning()方法是怎样唤醒线程的有些不明白那我们现在就进入最后一个类SimpleThread类它的源代码如下
//SimpleThreadjava
class SimpleThread extends Thread
{
private boolean runningFlag;
private String argument;
public boolean isRunning()
{
return runningFlag;
}
public synchronized void setRunning(boolean flag)
{
runningFlag = flag;
if(flag)
thisnotify();
}
public String getArgument()
{
return thisargument;
}
public void setArgument(String string)
{
argument = string;
}
public SimpleThread(int threadNumber)
{
runningFlag = false;
Systemoutprintln(thread + threadNumber + started);
}
public synchronized void run()
{
try{
while(true)
{
if(!runningFlag)
{
thiswait();
}
else
{
Systemoutprintln(processing + getArgument() + done);
sleep();
Systemoutprintln(Thread is sleeping);
setRunning(false);
}
}
} catch(InterruptedException e){
Systemoutprintln(Interrupt);
}
}//end of run()
}//end of class SimpleThread
如果你对JAVA的线程编程有些不太明白的话那我先在这里简单地讲解一下JAVA有一个名为Thread的类如果你要创建一个线程则必须要从Thread类中继承并且还要实现Thread类的run()接口要激活一个线程必须调用它的start()方法start()方法会自动调用run()接口因此用户必须在run()接口中写入自己的应用处理逻辑那么我们怎么来控制线程的睡眠与唤醒呢?其实很简单JAVA语言为所有的对象都内置了wait()和notify()方法当一个线程调用wait()方法时则线程进入睡眠状态就像停在了当前代码上了也不会继续执行它以下的代码了当调用notify()方法时则会从调用wait()方法的那行代码继续执行以下的代码这个过程有点像编译器中的断点调试的概念以本程序为例第行调用了wait()方法则这个线程就像凝固了一样停在了行上了如果我们在第行进行一个notify()调用的话那线程会从第行上唤醒继续从第行开始执行以下的代码了
通过以上的讲述我们现在就不难理解SimpleThread类了第-行通过设置一个标志runningFlag激活当前线程第-行是SimpleThread类的构造函数它用来告诉客户端启动的是第几号进程第-行则是我实现的run()接口它实际上是一个无限循环在循环中首先判断一下标志runningFlag如果没有runningFlag为false的话那线程处理睡眠状态否则第-行会进行真正的处理先打印用户键入的字符串然后睡眠秒钟为什么要睡眠秒钟呢?如果你不加上这句代码的话由于计算机处理速度远远超过你的键盘输入速度因此你看到的总是第号线程来处理你的请求从而达不到演示效果最后第行调用setRunning()方法又将线程置于睡眠状态等待新请求的到来
最后还有一点要注意的是如果你在一个方法中调用了wait()和notify()函数那你一定要将此方法置为同步的即synchronized否则在编译时会报错并得到一个莫名其妙的消息current thread not owner(当前线程不是拥有者)
至此为止我们完整地实现了一个线程池当然这个线程池只是简单地将客户端输入的字符串打印到了屏幕上而没有做任何处理对于一个真正的企业级运用本例还是远远不够的例如错误处理线程的动态调整性能优化临界区的处理客户端报文的定义等等都是值得考虑的问题但本文的目的仅仅只是让你了解线程池的概念以及它的简单实现如果你想成为这方面的高手本文是远远不够的你应该参考一些更多的资料来深入地了解它