zoukankan      html  css  js  c++  java
  • Java 线程池的原理与实现

    这几天主要是狂看源程序,在弥补了一些曾经知识空白的同一时候,也学会了不少新的知识(比方 NIO)。或者称为新技术吧。


    线程池就是当中之中的一个,一提到线程。我们会想到曾经《操作系统》的生产者与消费者。信号量,同步控制等等。
    一提到池。我们会想到数据库连接池。可是线程池又怎样呢?


    建议:在阅读本文前,先理一理同步的知识。特别是syncronized同步keyword的使用方法。
    关于我对同步的认识。要缘于大三年的一本书。书名好像是 Java 实战,这本书写得实在太妙了,真正的从理论到实践,从截图分析到.class字节码分析。哇,我想市场上非常难买到这么精致的书了。

    作为一个Java爱好者。我认为绝对值得一读。


    我对此书印象最深之中的一个的就是:equal()方法。由浅入深。经典!
    还有就是同步了,当中提到了我的几个编程误区。曾经怎样使用同步提高性能等等。通过学习,使我对同步的认识进一步加深。




    简介

        创建线程有两种方式:继承Thread或实现Runnable。Thread实现了Runnable接口,提供了一个空的run()方法,所以不论是继承Thread还是实现Runnable,都要有自己的run()方法。
        一个线程创建后就存在,调用start()方法就開始执行(执行run()方法),调用wait进入等待或调用sleep进入休眠期,顺利执行完成或休眠被中断或执行过程中出现异常而退出。

    wait和sleep比較:
          sleep方法有:sleep(long millis)。sleep(long millis, long nanos),调用sleep方法后,当前线程进入休眠期,暂停运行,但该线程继续拥有监视资源的全部权。到达休眠时间后线程将继续运行,直到完毕。

    若在休眠期还有一线程中断该线程,则该线程退出。
          wait方法有:wait()。wait(long timeout),wait(long timeout, long nanos),调用wait方法后,该线程放弃监视资源的全部权进入等待状态;
          wait():等待有其他的线程调用notify()或notifyAll()进入调度状态,与其他线程共同争夺监视。wait()相当于wait(0)。wait(0, 0)。
          wait(long timeout):当其他线程调用notify()或notifyAll(),或时间到达timeout亳秒。或有其他某线程中断该线程,则该线程进入调度状态。
          wait(long timeout, long nanos):相当于wait(1000000*timeout + nanos),仅仅只是时间单位为纳秒。





    线程池:
        多线程技术主要解决处理器单元内多个线程运行的问题,它能够显著降低处理器单元的闲置时间,添加处理器单元的吞吐能力。
       
        如果一个server完毕一项任务所需时间为:T1 创建线程时间,T2 在线程中运行任务的时间,T3 销毁线程时间。


       
        假设:T1 + T3 远大于 T2,则能够採用线程池,以提高server性能。
                    一个线程池包含下面四个基本组成部分:
                    1、线程池管理器(ThreadPool):用于创建并管理线程池。包含 创建线程池,销毁线程池。加入新任务。
                    2、工作线程(PoolWorker):线程池中线程。在没有任务时处于等待状态,能够循环的运行任务。
                    3、任务接口(Task):每一个任务必须实现的接口。以供工作线程调度任务的运行,它主要规定了任务的入口,任务运行完后的收尾工作,任务的运行状态等;
                    4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
                   
        线程池技术正是关注怎样缩短或调整T1,T3时间的技术,从而提高server程序性能的。它把T1,T3分别安排在server程序的启动和结束的时间段或者一些空暇的时间段,这样在server程序处理客户请求时,不会有T1,T3的开销了。

        线程池不仅调整T1,T3产生的时间段,并且它还显著降低了创建线程的数目,看一个样例:

        如果一个server一天要处理50000个请求,而且每一个请求须要一个单独的线程完毕。

    在线程池中,线程数通常是固定的,所以产生线程总数不会超过线程池中线程的数目,而如果server不利用线程池来处理这些请求则线程总数为50000。一般线程池大小是远小于50000。所以利用线程池的server程序不会为了创建50000而在处理请求时浪费时间。从而提高效率。




    /** 线程池类,工作线程作为其内部类 **/

    package org.ymcn.util;

    import java.util.Collections;
    import java.util.Date;
    import java.util.LinkedList;
    import java.util.List;

    import org.apache.log4j.Logger;

    /**
    * 线程池
    * 创建线程池,销毁线程池,加入新任务
    *
    * @author obullxl
    */
    public final class ThreadPool {
        private static Logger logger = Logger.getLogger(ThreadPool.class);
        private static Logger taskLogger = Logger.getLogger("TaskLogger");

        private static boolean debug = taskLogger.isDebugEnabled();
        // private static boolean debug = taskLogger.isInfoEnabled();
        /* 单例 */
        private static ThreadPool instance = ThreadPool.getInstance();

        public static final int SYSTEM_BUSY_TASK_COUNT = 150;
        /* 默认池中线程数 */
        public static int worker_num = 5;
        /* 已经处理的任务数 */
        private static int taskCounter = 0;

        public static boolean systemIsBusy = false;

        private static List<Task> taskQueue = Collections
                .synchronizedList(new LinkedList<Task>());
        /* 池中的全部线程 */
        public PoolWorker[] workers;

        private ThreadPool() {
            workers = new PoolWorker[5];
            for (int i = 0; i < workers.length; i++) {
                workers[i] = new PoolWorker(i);
            }
        }

        private ThreadPool(int pool_worker_num) {
            worker_num = pool_worker_num;
            workers = new PoolWorker[worker_num];
            for (int i = 0; i < workers.length; i++) {
                workers[i] = new PoolWorker(i);
            }
        }

        public static synchronized ThreadPool getInstance() {
            if (instance == null)
                return new ThreadPool();
            return instance;
        }
        /**
        * 添加新的任务
        * 每添加一个新任务,都要唤醒任务队列
        * @param newTask
        */
        public void addTask(Task newTask) {
            synchronized (taskQueue) {
                newTask.setTaskId(++taskCounter);
                newTask.setSubmitTime(new Date());
                taskQueue.add(newTask);
                /* 唤醒队列, 開始运行 */
                taskQueue.notifyAll();
            }
            logger.info("Submit Task<" + newTask.getTaskId() + ">: "
                    + newTask.info());
        }
        /**
        * 批量添加新任务
        * @param taskes
        */
        public void batchAddTask(Task[] taskes) {
            if (taskes == null || taskes.length == 0) {
                return;
            }
            synchronized (taskQueue) {
                for (int i = 0; i < taskes.length; i++) {
                    if (taskes[i] == null) {
                        continue;
                    }
                    taskes[i].setTaskId(++taskCounter);
                    taskes[i].setSubmitTime(new Date());
                    taskQueue.add(taskes[i]);
                }
                /* 唤醒队列, 開始运行 */
                taskQueue.notifyAll();
            }
            for (int i = 0; i < taskes.length; i++) {
                if (taskes[i] == null) {
                    continue;
                }
                logger.info("Submit Task<" + taskes[i].getTaskId() + ">: "
                        + taskes[i].info());
            }
        }
        /**
        * 线程池信息
        * @return
        */
        public String getInfo() {
            StringBuffer sb = new StringBuffer();
            sb.append(" Task Queue Size:" + taskQueue.size());
            for (int i = 0; i < workers.length; i++) {
                sb.append(" Worker " + i + " is "
                        + ((workers[i].isWaiting()) ? "Waiting." : "Running."));
            }
            return sb.toString();
        }
        /**
        * 销毁线程池
        */
        public synchronized void destroy() {
            for (int i = 0; i < worker_num; i++) {
                workers[i].stopWorker();
                workers[i] = null;
            }
            taskQueue.clear();
        }

        /**
        * 池中工作线程
        *
        * @author obullxl
        */
        private class PoolWorker extends Thread {
            private int index = -1;
            /* 该工作线程是否有效 */
            private boolean isRunning = true;
            /* 该工作线程能否够运行新任务 */
            private boolean isWaiting = true;

            public PoolWorker(int index) {
                this.index = index;
                start();
            }

            public void stopWorker() {
                this.isRunning = false;
            }

            public boolean isWaiting() {
                return this.isWaiting;
            }
            /**
            * 循环运行任务
            * 这或许是线程池的关键所在
            */
            public void run() {
                while (isRunning) {
                    Task r = null;
                    synchronized (taskQueue) {
                        while (taskQueue.isEmpty()) {
                            try {
                                /* 任务队列为空,则等待有新任务增加从而被唤醒 */
                                taskQueue.wait(20);
                            } catch (InterruptedException ie) {
                                logger.error(ie);
                            }
                        }
                        /* 取出任务运行 */
                        r = (Task) taskQueue.remove(0);
                    }
                    if (r != null) {
                        isWaiting = false;
                        try {
                            if (debug) {
                                r.setBeginExceuteTime(new Date());
                                taskLogger.debug("Worker<" + index
                                        + "> start execute Task<" + r.getTaskId() + ">");
                                if (r.getBeginExceuteTime().getTime()
                                        - r.getSubmitTime().getTime() > 1000)
                                    taskLogger.debug("longer waiting time. "
                                            + r.info() + ",<" + index + ">,time:"
                                            + (r.getFinishTime().getTime() - r
                                                    .getBeginExceuteTime().getTime()));
                            }
                            /* 该任务是否须要马上运行 */
                            if (r.needExecuteImmediate()) {
                                new Thread(r).start();
                            } else {
                                r.run();
                            }
                            if (debug) {
                                r.setFinishTime(new Date());
                                taskLogger.debug("Worker<" + index
                                        + "> finish task<" + r.getTaskId() + ">");
                                if (r.getFinishTime().getTime()
                                        - r.getBeginExceuteTime().getTime() > 1000)
                                    taskLogger.debug("longer execution time. "
                                            + r.info() + ",<" + index + ">,time:"
                                            + (r.getFinishTime().getTime() - r
                                                    .getBeginExceuteTime().getTime()));
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                            logger.error(e);
                        }
                        isWaiting = true;
                        r = null;
                    }
                }
            }
        }
    }

    /** 任务接口类 **/

    package org.ymcn.util;

    import java.util.Date;

    /**
    * 全部任务接口
    * 其它任务必须继承訪类
    *
    * @author obullxl
    */
    public abstract class Task implements Runnable {
        // private static Logger logger = Logger.getLogger(Task.class);
        /* 产生时间 */
        private Date generateTime = null;
        /* 提交运行时间 */
        private Date submitTime = null;
        /* 開始运行时间 */
        private Date beginExceuteTime = null;
        /* 运行完毕时间 */
        private Date finishTime = null;

        private long taskId;

        public Task() {
            this.generateTime = new Date();
        }

        /**
        * 任务运行入口
        */
        public void run() {
            /**
            * 相关运行代码
            *
            * beginTransaction();
            *
            * 运行过程中可能产生新的任务 subtask = taskCore();
            *
            * commitTransaction();
            *
            * 添加新产生的任务 ThreadPool.getInstance().batchAddTask(taskCore());
            */
        }

        /**
        * 全部任务的核心 所以特别的业务逻辑运行之处
        *
        * @throws Exception
        */
        public abstract Task[] taskCore() throws Exception;

        /**
        * 是否用到数据库
        *
        * @return
        */
        protected abstract boolean useDb();

        /**
        * 是否须要马上运行
        *
        * @return
        */
        protected abstract boolean needExecuteImmediate();

        /**
        * 任务信息
        *
        * @return String
        */
        public abstract String info();

        public Date getGenerateTime() {
            return generateTime;
        }

        public Date getBeginExceuteTime() {
            return beginExceuteTime;
        }

        public void setBeginExceuteTime(Date beginExceuteTime) {
            this.beginExceuteTime = beginExceuteTime;
        }

        public Date getFinishTime() {
            return finishTime;
        }

        public void setFinishTime(Date finishTime) {
            this.finishTime = finishTime;
        }

        public Date getSubmitTime() {
            return submitTime;
        }

        public void setSubmitTime(Date submitTime) {
            this.submitTime = submitTime;
        }

        public long getTaskId() {
            return taskId;
        }

        public void setTaskId(long taskId) {
            this.taskId = taskId;
        }

    }


     

  • 相关阅读:
    Leetcode 58. 最后一个单词的长度 双指针
    Leetcode 125. 验证回文串 双指针
    拜托,大厂做项目可不简单!
    被问懵了:一个进程最多可以创建多少个线程?
    面对祖传屎山代码应该采用的5个正确姿势
    VUE代码格式化配置vetur、eslint、prettier的故事
    如何快速实现一个虚拟 DOM 系统
    NodeJS 进程是如何退出的
    [堆][启发式合并]luogu P3261 [JLOI2015]城池攻占
    [Trie][堆]luogu P5283 [十二省联考2019]异或粽子
  • 原文地址:https://www.cnblogs.com/lxjshuju/p/6914310.html
Copyright © 2011-2022 走看看