mac_girl

线程池的简单实现

线程池的简单实现

一)、多线程的弊端

1).线程数量过大,耗尽CPU和内存资源。

2).线程的创建和关闭需要花费时间。

3).线程本身也要占用内存空间。

4).大量的线程回收会给GC带来压力,延长GC的停顿时间。

二)、线程池的作用

1).节省多线程在并发时不断创建和销毁线程所带来的额外开销,实现线程的复用。

2).使用线程池后,线程的创建和关闭由线程池维护。

三)、线程池的设计

1).TheadPool: 存储线程,维护线程的创建和关闭的线程池对象

结构:

 /*
    属性:
*    存放线程的集合: idleThreads, Vector类型
*    线程池的状态: isShoutdown, boolean类型
*    线程池中线程的个数: countThreads, int类型
*
*   方法:
*    rePool(): 回收线程,添加至线程池中
*    start(): 取出线程池中的线程,执行任务
*    shutdown(): 关闭线程
*
*    注: 线程的创建和关闭都是由线程池控制的
*/

2).PreThread: 一个与线程池配合永不退出的线程。

结构:

/**
      属性:
 *    所属的线程池对象: ThrealPool
 *    当前线程的闲置状态: isIdle , boolean类型
 *    当前线程的运行状态:isShutdown, Boolean
 *    线程任务对象: Runnable, target
 *
 *    方法:
 *    构造方法:public PreThread(Runable target, String name,                ThreadPool)
 *             target: 当前线程池里的线程要执行的任务
 *             name: 当前线程的名字
 *             ThreadPool: 当前线程的所属线程池
 *
 *    run(): 调用线程任务
 *    setTarget(Runable target): 复用线程池的对象,重启设置线程任                                  务。
 *    shutdown(): 关闭当前线程

四)、简单线程池的实现

ThreadTool: 线程池

/**
 * 线程池对象
 *    属性:
 *    存放线程的集合: idleThreads, Vector类型
 *    线程池的状态: isShoutdown, boolean类型
 *    线程池中线程的个数: countThreads, int类型
 *
 *    方法:
 *    rePool(): 回收线程,添加至线程池中
 *    start(): 取出线程池中的线程,执行任务
 *    shutdown(): 关闭线程
 *
 *    注: 线程的创建和关闭都是由线程池控制的
 */
public class ThreadPool {
    /**
     * 单例模式的线程池对象
     */
    private static ThreadPool instance;

    /**
     * 存放空闲线程的集合
     */
    private List<PreThread> idleThreads;

    /**
     * 线程池的状态
     */
    private boolean isShutdown;

    /**
     * 线程池中线程的个数
     */
    private int countThreads;

    /**
     * 私有化线程池构造方法,构建单例模式
     */
    private ThreadPool(){
       //刚开始,线程池的初始容量为5
       idleThreads = new Vector<PreThread>(5);
       //线程池里的线程个数为0
        countThreads = 0;
    }

    /**
     * 获取线程池实例
     */
    public static synchronized ThreadPool getInstance(){
        if(instance == null){
            instance = new ThreadPool();
        }
        return instance;
    }
    /**
     * 关闭线程池
     */
    public void shutdown(){
        //设置线程池的状态
        isShutdown = true;
        //关闭线程池里的所有线程
        for(int i = 0; i < idleThreads.size(); i++){
            PreThread preThread = idleThreads.get(i);
            preThread.shutdown();
        }
    }

    /**
     * 添加空闲线程至线程池
     */
    public void rePool(PreThread rePoolingThread){
        //判断当前线程池的状态
        if(!isShutdown){
            //如果线程池未关闭,将线程添加进线程池中
            idleThreads.add(rePoolingThread);
        }
        //若线程已关闭,则关闭当前线程
        rePoolingThread.shutdown();
    }

    /**
     * 开启任务线程
     */
    public void start(Runnable target){
        //判断线程池中是否有线程,有则复用,没有则创建
        if(idleThreads.size() > 0){
            //有线程则取出线程,重置线程任务
            int lastIndex = idleThreads.size() - 1;
            //取出线程
            PreThread preThread = idleThreads.get(lastIndex);
            //将该线程对象从线程池中移除
            idleThreads.remove(lastIndex);
            //重置线程任务,唤醒等待线程
            preThread.setTarget(target);
        }
        //如若线程池中没有闲置线程,则重新创建线程
        else{
            PreThread preThread = new PreThread(target,"preThread#"+countThreads,this);
            //开启任务线程,并将线程放入线程池
            preThread.start();
        }
    }
}

PreThread: 与线程池配合的永不退出的线程对象

/**
 * 与线程池配合的永不退出的线程
 *    属性:
 *    所属的线程池对象: ThrealPool
 *    当前线程的闲置状态: isIdle , boolean类型
 *    当前线程的运行状态:isShutdown, Boolean
 *    线程任务对象: Runnable, target
 *
 *    方法:
 *    构造方法:public PreThread(Runable target, String name, ThreadPool)
 *             target: 当前线程池里的线程要执行的任务
 *             name: 当前线程的名字
 *             ThreadPool: 当前线程的所属线程池
 *
 *    run(): 调用线程任务
 *    setTarget(Runable target): 复用线程池的对象,重启设置线程任务
 *    shutdown(): 关闭当前线程
 */
public class PreThread extends Thread{
    /**
     * 线程所属线程池
     */
    private ThreadPool pool;

    /**
     * 线程要执行的线程任务
     */
    private Runnable target;

    /**
     * 当前线程的闲置状态
     */
    private boolean isIdle = false;

    /**
     * 当前线程的状态
     */
    private boolean isShutdown = false;

    /**
     * 构造方法,指定线程池,线程任务,线程名字
     */
    public PreThread(Runnable target, String name, ThreadPool threadPool){
        super(name);
        this.target = target;
        this.pool = threadPool;
    }

    /**
     * 复用线程池的对象,重置线程任务
     */
    public void setTarget(Runnable target){
        this.target = target;
        //唤醒等待池的线程对象,由waiting状态转为Runable状态,去抢夺系统的资源
        synchronized (this) {
            //执行notifyAll()后,线程自行抢夺资源,直接进入运行状态,调用run()方法,不用显示的调用start()。
            notifyAll();
        }
    }

    /**
     * 关闭当前线程
     */
    public void shutdown(){
       //设置当前的线程为关闭状态,唤醒线程
        isShutdown = true;
        synchronized (this) {
            notifyAll();
        }
    }

    /**
     * 执行线程任务的逻辑
     */
    @Override
    public void run(){
        //判断当前的线程是否关闭
        //线程未关闭,执行任务线程的业务逻辑
        if(!isShutdown){
            //设置线程的状态,非闲置状态
            isIdle = false;
            //执行任务线程
            target.run();
            //重新设置线程状态为空闲状态
            isIdle = true;
            //添加当前线程进入线程池
            pool.rePool(this);
            //执行完任务,线程进入等待状态
            try {
   /**
   * 解释一下,这里为什么要加入代码块:
   * 因为wait()/notify()/notifyAll()是配合锁对象使用的。
   * 1).如果线程要调用对象的wait()方法,必须首先获得该对象的监视器            锁,调用wait()之后 当前线程又立即释放掉锁,线程随后进入              WAIT_SET(等待池)中。
   * 2).如果线程要调用对象的notify()/notifyAll()方法,也必须先获得         对象的监视器锁调用方法之后,立即释放掉锁然后处于Wait_set的线         程被转移到Entry_set(等锁池)中去竞争锁资源.。The Winner           Thread,也就是成功获得了对象的锁的线程,就是对象锁的拥有者,
        会进入runnable状态。
    *3).由于需要获得锁之后才能够调用wait()/notify()方法,因此必须将         它们放到同步代码块中
     */
                synchronized (this) {
                    //线程进入等待状态,进入到Wait Set中,并释放锁
                    wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

五)、使用线程池和不使用线程池的性能比较

ThreadTask: 线程任务类

/**
 * 线程任务类
 */
public class ThreadTask implements Runnable{
    private String name;
    public ThreadTask(String name){
       this.name = name;
    }
    @Override
    public void run() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

ThreadTest: 测试类
自定义线程池对象:

/**
 * 比较实用线程池和不使用线程池的性能
 */
public class ThreadTest {
    public static void main(String[] args) {
        /**
         * 不使用线程池
         */
        long start0 = System.currentTimeMillis();
        for(int i = 0; i < 1000; i++){
            new Thread(new ThreadTask("testThread#"+i)).start();
        }
        System.out.println("不使用线程池所花费的时间:"+(System.currentTimeMillis() - start0));

        /**
         * 实用线程池
         */
        //构造线程池
        ThreadPool pool = ThreadPool.getInstance();
        long start1 = System.currentTimeMillis();
        for(int i = 0; i < 100; i++){
            pool.start(new ThreadTask("testThread#"+i));
        }
        System.out.println("使用线程池所花费的时间:"+(System.currentTimeMillis() - start1));
    }

}

结果:

不使用线程池所花费的时间:63
使用线程池所花费的时间:7

使用JDK自定义的线程对象:

/**
 * 使用JDK自带的线程池:
 *
 *
 */
public class JDK_ThreadPool {
    public static void main(String[] args) {
        /**
         * 不使用线程池
         */
        long start0 = System.currentTimeMillis();
        for(int i = 0; i < 1000; i++){
            new Thread(new ThreadTask("testThread#"+i)).start();
        }
        System.out.println("不使用线程池所花费的时间:"+(System.currentTimeMillis() - start0));

        /**
         * 使用线程池
         */
        //JDK自带的线程池
        //ThreadPool pool = ThreadPool.getInstance();
        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
        long start1 = System.currentTimeMillis();
        for(int i = 0; i < 100; i++){
            //pool.start(new ThreadTask("testThread#"+i));
            executor.execute(new ThreadTask("testThread#"+i));
        }
        System.out.println("使用线程池所花费的时间:"+(System.currentTimeMillis() - start1));
    }
}

结果:

不使用线程池所花费的时间:62
使用线程池所花费的时间:7

结论: 不使用线程池的线程需创建1000个线程对象,使用线程池创建的线程对象小于1000个,故使用线程池的性能优于不使用线程池的性能。

posted on 2019-10-21 16:13  宇宙美少女  阅读(286)  评论(0编辑  收藏  举报

导航