Java之线程池深度剖析

1.线程池的引入
  引入的好处:
  1)提升性能。创建和消耗对象费时费CPU资源
  2)防止内存过度消耗。控制活动线程的数量,防止并发线程过多。
  使用条件:
     假设在一台服务器完成一项任务的时间为T
     T1 创建线程的时间    
     T2 在线程中执行任务的时间,包括线程间同步所需时间    
     T3 线程销毁的时间     
     显然T = T1+T2+T3。注意这是一个极度简化的假设。
     可以看出T1,T3是多线程本身的带来的开销,我们渴望减少T1,T3所用的时间,从而减少T的时间。但一些线程的使用者并没有注意到这一点,所以在程序中频繁的创建或销毁线程,这导致T1和T3在T中占有相当比例。显然这是突出了线程的弱点(T1,T3),而不是优点(并发性)。
     线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。
     线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目。

     在Android中当同时并发多个网络线程时,引入线程池技术会极大地提高APP的性能。

2.线程池例子
 1)JDK自身带有线程池的实现类ThreadPoolExecutor
 2)下面是一个模拟ThreadPoolExecutor的例子,以加深对原理的理解

public final class ThreadPool {
     // 线程池中默认线程的个数为5
     private static int worker_num = 5;
     // 工作线程
     private WorkThread[] workThreads;
    
     // 任务队列,作为一个缓冲,List线程不安全
     private List<Runnable> taskQueue = new LinkedList<Runnable>();

     private static ThreadPool threadPool;

     // 创建具有默认线程个数的线程池
     private ThreadPool() {
          this(5);
     }

     // 创建线程池,worker_num为线程池中工作线程的个数
     private ThreadPool(int worker_num) {
          ThreadPool.worker_num = worker_num;
          workThreads = new WorkThread[worker_num];
          for (int i = 0; i < worker_num; i++) {
               workThreads[i] = new WorkThread();
               workThreads[i].start();// 开启线程池中的线程
          }
     }

     // 单态模式,获得一个默认线程个数的线程池
     public static ThreadPool getThreadPool() {
          return getThreadPool(ThreadPool.worker_num);
     }

     // 单态模式,获得一个指定线程个数的线程池,worker_num(>0)为线程池中工作线程的个数
     // worker_num<=0创建默认的工作线程个数
     public static ThreadPool getThreadPool(int worker_num1) {
          if (threadPool == null)
               threadPool = new ThreadPool(worker_num1);
          return threadPool;
     }

     // 执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定
     public void addTask(Runnable task) {
          synchronized (taskQueue) {
               taskQueue.add(task);
               taskQueue. notifyAll();
          }
     }

     // 销毁线程池,该方法保证在所有任务都完成的情况下才销毁所有线程,否则等待任务完成才销毁
     public void destroy() {
          while (!taskQueue.isEmpty()) {// 如果还有任务没执行完成,就先睡会吧
               try {
                    Thread.sleep(10);
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }
          }
          // 工作线程停止工作,且置为null
          for (int i = 0; i < worker_num; i++) {
               workThreads[i].stopWorker();
               workThreads[i] = null;
          }
          threadPool=null;
          taskQueue.clear();// 清空任务队列
     }

     /**
      * 内部类,工作线程
      */
     private class WorkThread extends Thread {
          // 该工作线程是否有效,用于结束该工作线程
          private boolean isRunning = true;

          /*
           * 关键所在啊,如果任务队列不空,则取出任务执行,若任务队列空,则等待
           */
          @Override
          public void run() {
               Runnable r = null;
               while (isRunning) {// 注意,若线程无效则自然结束run方法,该线程就没用了
                    synchronized (taskQueue) {
                         while (isRunning && taskQueue.isEmpty()) {// 队列为空
                              try {
                                   taskQueue.wait(20);
                              } catch (InterruptedException e) {
                                   e.printStackTrace();
                              }
                         }
                         if (!taskQueue.isEmpty())
                              r = taskQueue.remove(0);// 取出任务
                    }
                    if (r != null) {
                         r.run();// 执行任务
                    }
                    r = null;
               }
          }

          // 停止工作,让该线程自然执行完run方法,自然结束
          public void stopWorker() {
               isRunning = false;
          }
     }
}
posted on 2014-07-20 14:31  Devin Zhang  阅读(28201)  评论(1编辑  收藏  举报