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;
          }
     }
}
相關文章
相關標籤/搜索