无限制线程的缺陷
多线程的软件设计方法确实可以最大限度地发挥多核处理器的计算能力,提高生产系统的吞吐量和性能。但是,若不加控制和管理的随意使用线程,对系统的性能反而会产生不利的影响。
一种最为简单的线程创建和回收的方法类似如下:
new Thread(new Runnable() { @Override public void run() { //do sth } }).start();
以上代码创建了一条线程,并在run()方法结束后,自动回收该线程。在简单的应用系统中,这段代码并没有太多问题。但是在真实的生产环境中,系统由于真实环境的需要,可能会开启很多线程来支撑其应用。而当线程数量过大时,反而会耗尽CPU和内存资源。
首先,虽然与进程相比,线程是一种轻量级的工具,但其创建和关闭依然需要花费时间,如果为每一个小的任务都创建一个线程,很有可能出现创建和销毁线程所占用的时间大于该线程真实工作所消耗的时间,反而会得不偿失。
其次,线程本身也是要占用内存空间的,大量的线程会抢占宝贵的内部资源。
因此,在实际生产环境中,线程的数量必须得到控制。盲目地大量创建线程对系统性能是有伤害的。
简单的线程池实现
下面给出一个最简单的线程池,该线程池不是一个完善的线程池,但已经实现了一个基本线程池的核心功能,有助于快速理解线程池的实现。
1、线程池的实现
public class ThreadPool { private static ThreadPool instance = null;
//空闲的线程队列 private List<PThread> idleThreads; //已有的线程总数 private int threadCounter; private boolean isShutDown = false;
private ThreadPool() { this.idleThreads = new Vector<>(5); threadCounter = 0; }
public int getCreatedThreadCounter() { return threadCounter; }
//取得线程池的实例 public synchronized static ThreadPool getInstance() { if (instance == null) { instance = new ThreadPool(); } return instance; }
//将线程池放入池中 protected synchronized void repool(PThread repoolingThread) { if (!isShutDown) { idleThreads.add(repoolingThread); } else { repoolingThread.shutDown(); } }
//停止池中所有线程 public synchronized void shutDown() { isShutDown = true; for (int threadIndex = 0; threadIndex < idleThreads.size(); threadIndex++) { PThread pThread = idleThreads.get(threadIndex); pThread.shutDown(); } }
//执行任务 public synchronized void start(Runnable target) { PThread thread = null; //如果有空闲线程,则直接使用 if (idleThreads.size() > 0) { int lastIndex = idleThreads.size() - 1; thread = idleThreads.get(lastIndex); idleThreads.remove(thread); //立即执行这个任务 thread.setTarget(target); }//没有空闲线程,则创建线程 else { threadCounter++; //创建新线程 thread = new PThread(target, "PThread #" + threadCounter, this); //启动这个线程 thread.start(); } }
}
2、要实现上面的线程池,就需要一个永不退出的线程与之配合。PThread就是一个这样的线程。它的主体部分是一个无限循环,该线程在手动关闭前永不结束,并一直等待新的任务到达。
public class PThread extends Thread { //线程池 private ThreadPool pool; //任务 private Runnable target; private boolean isShutDown = false; private boolean isIdle = false; //是否闲置 //构造函数 public PThread(Runnable target,String name, ThreadPool pool){ super(name); this.pool = pool; this.target = target; }
public Runnable getTarget(){ return target; }
public boolean isIdle() { return isIdle; }
@Override public void run() { //只要没有关闭,则一直不结束该线程 while (!isShutDown){ isIdle = false; if (target != null){ //运行任务 target.run(); } try { //任务结束了,到闲置状态 isIdle = true; pool.repool(this); synchronized (this){ //线程空闲,等待新的任务到来 wait(); } } catch (InterruptedException e) { e.printStackTrace(); } isIdle = false; } }
public synchronized void setTarget(Runnable newTarget){ target = newTarget; //设置了任务之后,通知run方法,开始执行这个任务 notifyAll(); }
//关闭线程 public synchronized void shutDown(){ isShutDown = true; notifyAll(); }
}
3、测试Main方法
public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 1000; i++) { ThreadPool.getInstance().start(new Runnable() { @Override public void run() { try { //休眠100ms Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }); } }
线程池如此常用,以致于jdk自带了一些线程池框架,例如 ThreadPoolExecutor,以及基于ThreadPoolExecutor实现的Executor框架 。关于线程池框架的介绍,限于篇幅,等待下回分解。
|