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;
          }
     }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM