• 线程池的简单实现


    线程池的简单实现

    一)、多线程的弊端

    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个,故使用线程池的性能优于不使用线程池的性能。

    金麟岂能忍一世平凡 飞上了青天 天下还依然
  • 相关阅读:
    Windows下Tomcat配置虚拟路径
    Windows下Tomcat配置虚拟主机
    Windows下Tomcat的下载安装与配置
    Windows系统下Jdk的下载安装与配置
    SpringBoot项目中Swagger的配置和使用
    Windows 10通过指定端口进行远程访问的防火墙设置
    Java反射
    Java导出Pdf格式表单
    排序
    二叉查找树
  • 原文地址:https://www.cnblogs.com/Auge/p/11714174.html
Copyright © 2020-2023  润新知