1 引入线程池
虽然,线程是一个轻量级的进程,但是其创建和关闭仍然需要花费一定的时间,耗费CPU,如果为每个任务不管大小都去分配一个线程,很有可能创建和销毁线程所需的时间大于该线程实际执行任务的时间,极大浪费CPU资源。
而且,线程也是要占用内存空间的,大量的线程会占用宝贵的内存资源。如果线程创建过多,很可能会出现Out of Memory异常。而且,大量的线程回收也会给GC带来很大压力。
因此,对线程的使用必须掌握一个度,在有限的范围内,增加线程的数量可以明显的提升系统的吞吐量,但是,超过一定阈值后,反而会降低整个系统的效率,浪费系统资源。
在实际生产环境中,线程的数量必须得到控制,盲目地大量创建线程对系统性能是有伤害的。
线程池的作用就是通过线程复用来限制系统中线程的数量。当系统接受一个新任务时,需要一个线程时,并不急于去创建新的线程,而是先去线程池里面查看是否有空余的线程,如果有,则直接使用,如果没有或者阻塞获取创建新的线程(取决于使用的是何种类型的线程池)。当任务完成后,并不将线程销毁,而是将其放回线程池的空闲队列,等待下次使用。
2 自己实现简单的线程池
(1)ThreadPool.java:
public class ThreadPool {
/**
* 静态实例
*/
private static ThreadPool pool;
/**
* 空闲线程列表
*/
private List<MyThread> idelThreads;
/**
* 当前已创建的线程数
*/
private int count;
/**
* 判断线程池是否关闭
*/
private boolean isShutDown=false;
private ThreadPool(){
idelThreads=new ArrayList<MyThread>();
count=0;
}
/**
* 静态方法 获得线程池实例
* @return
*/
public synchronized static ThreadPool getInstance() {
if(pool==null){
pool=new ThreadPool();
}
return pool;
}
/**
* 添加线程到线程池
* 因为是多线程的,可能一个线程已经将线程池关闭,而其他线程要向线程池里面添加线程
* 所以向里面添加线程时,先判断线程池是否关闭
* @param thread
*/
public synchronized void addThread(MyThread thread){
if(!isShutDown){
idelThreads.add(thread);
}else {
thread.shutDown();
}
}
/**
* 关闭线程池,把池子中的线程的状态都置为shutdown,
* 同时修改线程池的状态
*/
public synchronized void shutDown(){
for (MyThread myThread : idelThreads) {
myThread.shutDown();
}
isShutDown=true;
}
/**
* 执行任务,不需要直接和线程打交道,而只需要和线程池打交道
* 线程的管理由线程池全权负责
* @param task
*/
public synchronized void execute(Runnable task) {
MyThread thread=null;
/**
* 如果有空闲线程
* (1)获取最后一个空闲线程
* (2)将其删除
* (3)setTask,执行任务
*/
if(idelThreads.size()>0){
thread=idelThreads.remove(idelThreads.size()-1);
thread.setTask(task);
}else {
/**
* 没有空闲线程,则创建新线程
*/
count++;
/**
* 因为这个线程要执行任务,所以并没有加入到空闲队列中
*/
thread=new MyThread(this,task,"MyThread"+count);
/**
* 启动这个线程
* 在run函数中,任务执行完毕后,会加入空闲队列
*/
thread.start();
}
}
public synchronized List<MyThread> getThreads() {
return idelThreads;
}
/**
* 返回线程池中的当前线程数
* @return
*/
public synchronized int getCount() {
return count;
}
/**
* 某个线程shutDown后,应该从线程池删除
* @param thread
*/
public synchronized void removeThread(MyThread thread) {
idelThreads.remove(thread);
count--;
}
}
(2)MyThread.java:
public class MyThread extends Thread {
/**
* 线程是否关闭
*/
private boolean isShutDown=false;
/**
* 该线程要执行的任务
*/
private Runnable task=null;
/**
* 该线程所属的线程池
*/
private ThreadPool pool=null;
public MyThread(ThreadPool pool,Runnable task,String name) {
// TODO Auto-generated constructor stub
super(name);
this.pool=pool;
this.task=task;
}
/**
* 关闭该线程,唤醒空闲等待新任务的线程
* 同时,应该将该线程从线程池中删除
*/
public synchronized void shutDown(){
isShutDown=true;
pool.removeThread(this);
notifyAll();
}
/**
* 给线程分配任务
* 唤醒空闲等待中的进程
* @param task
*/
public synchronized void setTask(Runnable task){
this.task=task;
notifyAll();
}
/**
* 最关键的一个函数
*/
@Override
public void run() {
// TODO Auto-generated method stub
/**
* 只要没有关闭,则该线程在完成任务后阻塞,等待接受新任务
*/
while (!isShutDown) {
/**
* 如果已经分配了任务,则直接执行
* 主要直接调用task的run方法,而不是通过new thread()去执行,因为线程由线程池管理
*/
if (task!=null) {
task.run();
}
/**
* 任务执行结束,将线程加入空闲队列,然后在池子中等待新任务的到来
*/
pool.addThread(this);
/**
* wait必须位于synchronized块中
*/
synchronized (this) {
try {
/**
* 会被setTask唤醒,从池子中将这个线程取出,执行新任务
* 也可能会被shutDown关闭该线程唤醒
*/
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
(3)Task.java:
public class Task implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
(4)Main.java:
public class Main {
public static void main(String[] args) {
for(int i=0;i<1000;i++){
new Thread(new Task()).start();
}
ThreadPool pool=ThreadPool.getInstance();
for(int i=0;i<1000;i++){
pool.execute(new Task());
}
}
}
下一篇我会介绍一下JDK自带的比较完善的线程池实现和使用方法。