关于Java线程池



在简单的应用场景中我们可以直接使用new Thread()的形式去创建一个多线程任务,但是在真实的的生产环境中,系统可能会开启很多线程来支撑应用。然而当线程数量过大时,反而会耗尽CPU和内存资源。

虽然与进程相比,线程是一种轻量级的工具,但其创建和关闭仍然需要消耗时间,如果为每一个小的任务都创建一个线程,很可能会得不偿失。其次线程本身也要占用资源。大量线程抢夺资源,如果处理不当,可能会导致Out of Memory,即使没有,大量的线程回收也会给GC带来很大的压力。延长GC的停顿时间。

为了节省系统在多线程并发下时不断地创建和销毁线程带来的额外开销。需要引入线程池。线程池的基本功能就是线程复用。当线程接收到一个任务是时,并不急于立即去创建新的线程,而是去线程池查找是否有空闲的线程,如果有,则直接使用线程池中的线程工作,如果没有,再去创建新的线程。待任务完成后,也不是销毁线程,而是将线程放入到线程池中的等待队列,等到下次使用的时候再次调用。

下面各出一个简单的线程池的实现Demo,助于理解线程池的实现。

首先是线程池的实现:

public class ThreadPool {
    private static ThreadPool instance = null;
    //空闲的线程队列
    private List<PThread> idleThreads;
    //已有的线程总数
    private int threadCoounter;
    private boolean isShutdown = false;

    private ThreadPool(){
        this.idleThreads = new Vector<PThread>();
        threadCoounter = 0;
    }

    public int getCreatedThreadsCount(){
        return threadCoounter;
    }

    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 i = 0; i < idleThreads.size(); i++) {
            idleThreads.get(i).shutDown();
        }
    }

    //执行任务
    public synchronized void start(Runnable target){
        PThread pThread = null;
        if (idleThreads.size() > 0){
            pThread = idleThreads.get(idleThreads.size()-1);
            idleThreads.remove(idleThreads.size()-1);
            //立即执行这个任务
            pThread.setTarget(target);
        }else {//没有空闲线程
            threadCoounter++;
            pThread = new PThread(target, "PThread #" + threadCoounter, this);
            //启动线程
            pThread.start();

        }
    }
}

要使用上面的线程池,如要有一个永不退出的线程与之配合。。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 threadPool) {
        super(name);
        this.pool = threadPool;
        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();
            }
            //任务结束 置为空闲状态
            isIdle = true;
            try {
                //任务结束后不是关闭线程 而是放入到线程池的空闲队列
                pool.repool(this);
                synchronized (this){
                    //线程空闲 等待新的任务列表
                    wait();
                }
            }catch (InterruptedException ex){
                ex.printStackTrace();
            }
            isIdle = false;
        }
    }

    //关闭线程
    public void shutDown() {
        isShutdown = true;
        notifyAll();
    }

    public synchronized void setTarget(Runnable newTarget) {
        this.target = newTarget;
        //设置了新的任务后 通知run方法,开始执行这个任务
        notifyAll();
    }
}

使用的时候直接使用 ThreadPool.getInstance().start(Runnable run);

当然这只是个例子,实际上JDK5以上提供了一套Exectuor框架,可以使用

ExecutorService service = Executors.newCachedThreadPool();
Executors.newFixedThreadPool(10);
Executors.newSingleThreadExecutor();
Executors.newScheduledThreadPool(5);
Executors.newWorkStealingPool();
Executors.newSingleThreadScheduledExecutor();

来创建一种适合的线程池,然后使用service.submit(Runnable/Callable)来提交一个任务到线程池。

参考:《大话Java性能优化》