java併發編程的藝術(四)-----線程池

線程池技術產生背景

在應用服務器端,需要大量處理來自用戶的任務,如果每接收到一個任務服務器端採取採用一個新的線程來處理,然後等到線程結束又關閉,這會造成大量的上下文切換,並且線程的創建和銷燬帶來的資源佔用也很巨大,這就造成了資源浪費。我們通過採用線程池的技術,預先創建若干數量的線程,並且對這些線程的管理不交給用戶,因此可以不斷地重複利用創建好了的線程去執行任務,從而避免了線程創建和銷燬的資源損耗,並減少了上下文切換,因爲線程數減少了嘛。

線程池帶來的好處

  • 降低資源損耗
  • 提高響應速度
  • 提高線程的可管理性

線程池的實現原理

線程池的處理流程:
(1)線程池判斷核心線程池裏面是否所有線程都處於工作狀態,如果不是就創建一個新的線程執行任務
(2)線程池判斷工作隊列是否滿了,沒滿就將任務放入工作隊列中
(3)線程池判斷線程池裏面是否所有線程都處於工作狀態,如果不是則創建一個新的線程執行任務
(4)無法執行任務
線程池執行任務的方法源碼分析(JDK8裏面的源碼):

public void execute(Runnable command) {
/*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
   //如果當前線程數小於核心線程數,創建線程並立即執行任務
        if (workerCountOf(c) < corePoolSize) {
            //自動檢驗線程的工作狀態和工作數量
            if (addWorker(command, true))
                return;
      //獲取處於工作狀態的線程數
            c = ctl.get();
        }
        //雙重檢驗是否應該將任務加入工作隊列,因爲存在第一次檢查之後其他線程死亡的情況以及線程池被關閉的狀況
        //確保沒有線程死亡
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //新任務已經被運行了,將任務移除任務隊列
            if (! isRunning(recheck) && remove(command))
                reject(command);
            //線程池被關閉情況
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //未達到最大線程數,再次新增一個線程進行任務處理,失敗說明線程池拒絕執行超負荷任務
        else if (!addWorker(command, false))
          reject(command);
    }

工作線程會將線程封裝成工作線程Work,並在run方法裏面不斷獲取工作隊列裏的任務來執行

合理配置線程池

對於CPU密集型的服務器,應該配置儘可能少的線程,如N(cpu數量)+1個線程的線程池,對於IO密集型任務,應配置儘可能多的線程,如2×N(cpu數量)線程的線程池。因爲IO密集,需要更長的時間等待處理IO操作,如果線程數太少,此時CPU就會處於空閒狀態,並且還有很多的任務等待執行,執行效率很低。

線程池監控

通過監控線程池需要執行的任務數量(taskCount)、以執行的任務數量(completedTaskCount)、最大線程創建情況(largestPoolSize)等數據可得到該線程池的吞吐量以及總結出最佳的配置參數。

發佈了45 篇原創文章 · 獲贊 10 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章