线程池
- 常见使用场景
- Web/数据库/文件/邮件 服务器
- 请求来自远程的客户端
- 请求数量很大, 任务执行时间较短
一个请求一个线程
- 创建线程,销毁线程的开销比较大
- 太多的线程导致内存耗尽
复用一个资源的技术,前提是请求数量很大, 任务执行时间较短
线程池
- 系统预先创建指定数量的线程
- 对多任务重用线程, 线程创建的开销被分摊
- 请求到来时, 线程已经存在, 立刻就可以服务
- 通过限制线程池中的线程数量, 防止资源不足
关于线程池的思考
- 当线程池刚刚创立,还没有Task到来的时候, 池中的线程处于什么状态?
- 阻塞状态(block)
- 当Task到来的时候,线程池中的线程如何得到通知?
- notify(),任务来了就唤醒某一个线程
- 当线程池中的线程完成工作,如何回到池中?
- 回去阻塞队列中取任务,如果发现没有任务就会放入线程池
- Task是个什么东西?
- 自定义的结构能被线程识别
线程池的本质就是一个阻塞队列
线程池例子: WorkerThread
通过下面这个例子了解线程池的原理
public interface Task {
public void execute();
}
public class ThreadPool {
private BlockingQueue taskQueue = null;
private List<WorkerThread> threads = new ArrayList<WorkerThread>();
private boolean isStopped = false;
public ThreadPool( int numOfThreads, int maxNumOfTasks ){
//阻塞队列
taskQueue = new BlockingQueue( maxNumOfTasks );
for( int i=0; i<numOfThreads; i++ ){
//将阻塞队列放入线程池中
threads.add( new WorkerThread( taskQueue ) );
}
//把所有线程都启动
for (WorkerThread thread : threads) {
thread.start();
}
}
public synchronized void execute( Task task ) throws Exception{
if(this.isStopped)
throws IllegalStateException("ThreadPool is stopped");
//添加任务
this.taskQueue.enqueue(task);
}
public synchronized void stop(){
this.isStopped = true;
for(WorkerThread thread : threads){
thread.doStop();
}
}
}
public class WorkerThread extends Thread {
private BlockingQueue taskQueue = null;
private boolean isStopped = false;
public WorkerThread( BlockingQueue queue ) {
taskQueue = queue;
}
public void run(){
while( !isStopped ){
try{
//核心:下面的调用如果没有取到任务会阻塞
Task task = (Task)taskQueue.dequeue();
task.execute();
} catch (Exception e){
}
}
}
public synchronized void doStop(){
isStopped = true;
//为什么调用中断的方法:因为某些任务可能处于阻塞的状态。
//跳出wait调用。
this.interrupt();
}
public synchronized boolean isStopped(){
return isStopped;
}
}