成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

JAVA線程池原理實例詳解

本文實例講述了JAVA線程池原理。分享給大家供大家參考,具體如下:

鹽山ssl適用于網(wǎng)站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:028-86922220(備注:SSL證書合作)期待與您的合作!

線程池的優(yōu)點

1、線程是稀缺資源,使用線程池可以減少創(chuàng)建和銷毀線程的次數(shù),每個工作線程都可以重復使用。

2、可以根據(jù)系統(tǒng)的承受能力,調(diào)整線程池中工作線程的數(shù)量,防止因為消耗過多內(nèi)存導致服務器崩潰。

線程池的創(chuàng)建

public ThreadPoolExecutor(int corePoolSize,
               int maximumPoolSize,
               long keepAliveTime,
               TimeUnit unit,
               BlockingQueue<Runnable> workQueue,
               RejectedExecutionHandler handler) 
  • corePoolSize:線程池核心線程數(shù)量
  • maximumPoolSize:線程池最大線程數(shù)量
  • keepAliverTime:當活躍線程數(shù)大于核心線程數(shù)時,空閑的多余線程最大存活時間
  • unit:存活時間的單位
  • workQueue:存放任務的隊列
  • handler:超出線程范圍和隊列容量的任務的處理程序

線程池的實現(xiàn)原理

提交一個任務到線程池中,線程池的處理流程如下:

1、判斷線程池里的核心線程是否都在執(zhí)行任務,如果不是(核心線程空閑或者還有核心線程沒有被創(chuàng)建)則創(chuàng)建一個新的工作線程來執(zhí)行任務。如果核心線程都在執(zhí)行任務,則進入下個流程。

2、線程池判斷工作隊列是否已滿,如果工作隊列沒有滿,則將新提交的任務存儲在這個工作隊列里。如果工作隊列滿了,則進入下個流程。

3、判斷線程池里的線程是否都處于工作狀態(tài),如果沒有,則創(chuàng)建一個新的工作線程來執(zhí)行任務。如果已經(jīng)滿了,則交給飽和策略來處理這個任務。

JAVA線程池原理實例詳解

線程池的源碼解讀

1、ThreadPoolExecutor的execute()方法

public void execute(Runnable command) {
    if (command == null)
      throw new NullPointerException(); //如果線程數(shù)大于等于基本線程數(shù)或者線程創(chuàng)建失敗,將任務加入隊列
    if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {//線程池處于運行狀態(tài)并且加入隊列成功
      if (runState == RUNNING && workQueue.offer(command)) {
        if (runState != RUNNING || poolSize == 0)
          ensureQueuedTaskHandled(command);
      }//線程池不處于運行狀態(tài)或者加入隊列失敗,則創(chuàng)建線程(創(chuàng)建的是非核心線程)
      else if (!addIfUnderMaximumPoolSize(command))//創(chuàng)建線程失敗,則采取阻塞處理的方式
        reject(command); // is shutdown or saturated
    }
}

2、創(chuàng)建線程的方法:addIfUnderCorePoolSize(command)

private boolean addIfUnderCorePoolSize(Runnable firstTask) {
    Thread t = null;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
      if (poolSize < corePoolSize && runState == RUNNING)
        t = addThread(firstTask);
    } finally {
      mainLock.unlock();
    }
    if (t == null)
      return false;
    t.start();
    return true;
}

我們重點來看第7行:

private Thread addThread(Runnable firstTask) {
    Worker w = new Worker(firstTask);
    Thread t = threadFactory.newThread(w);
    if (t != null) {
      w.thread = t;
      workers.add(w);
      int nt = ++poolSize;
      if (nt > largestPoolSize)
        largestPoolSize = nt;
    }
    return t;
}

這里將線程封裝成工作線程worker,并放入工作線程組里,worker類的方法run方法:

public void run() {
  try {
    Runnable task = firstTask;
    firstTask = null;
    while (task != null || (task = getTask()) != null) {
      runTask(task);
      task = null;
    }
  } finally {
    workerDone(this);
  }
}

worker在執(zhí)行完任務后,還會通過getTask方法循環(huán)獲取工作隊里里的任務來執(zhí)行。

我們通過一個程序來觀察線程池的工作原理:

1、創(chuàng)建一個線程

public class ThreadPoolTest implements Runnable
{
  @Override
  public void run()
  {
    try
    {
      Thread.sleep(300);
    }
    catch (InterruptedException e)
    {
      e.printStackTrace();
    }
  }
}

2、線程池循環(huán)運行16個線程:

public static void main(String[] args)
{
    LinkedBlockingQueue<Runnable> queue =
      new LinkedBlockingQueue<Runnable>(5);
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, queue);
    for (int i = 0; i < 16 ; i++)
    {
      threadPool.execute(
        new Thread(new ThreadPoolTest(), "Thread".concat(i + "")));
      System.out.println("線程池中活躍的線程數(shù): " + threadPool.getPoolSize());
      if (queue.size() > 0)
      {
        System.out.println("----------------隊列中阻塞的線程數(shù)" + queue.size());
      }
    }
    threadPool.shutdown();
}

執(zhí)行結果:

線程池中活躍的線程數(shù): 1
線程池中活躍的線程數(shù): 2
線程池中活躍的線程數(shù): 3
線程池中活躍的線程數(shù): 4
線程池中活躍的線程數(shù): 5
線程池中活躍的線程數(shù): 5
----------------隊列中阻塞的線程數(shù)1
線程池中活躍的線程數(shù): 5
----------------隊列中阻塞的線程數(shù)2
線程池中活躍的線程數(shù): 5
----------------隊列中阻塞的線程數(shù)3
線程池中活躍的線程數(shù): 5
----------------隊列中阻塞的線程數(shù)4
線程池中活躍的線程數(shù): 5
----------------隊列中阻塞的線程數(shù)5
線程池中活躍的線程數(shù): 6
----------------隊列中阻塞的線程數(shù)5
線程池中活躍的線程數(shù): 7
----------------隊列中阻塞的線程數(shù)5
線程池中活躍的線程數(shù): 8
----------------隊列中阻塞的線程數(shù)5
線程池中活躍的線程數(shù): 9
----------------隊列中阻塞的線程數(shù)5
線程池中活躍的線程數(shù): 10
----------------隊列中阻塞的線程數(shù)5
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task Thread[Thread15,5,main] rejected from java.util.concurrent.ThreadPoolExecutor@232204a1[Running, pool size = 10, active threads = 10, queued tasks = 5, completed tasks = 0]
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
    at test.ThreadTest.main(ThreadTest.java:17)

從結果可以觀察出:

1、創(chuàng)建的線程池具體配置為:核心線程數(shù)量為5個;全部線程數(shù)量為10個;工作隊列的長度為5。
2、我們通過queue.size()的方法來獲取工作隊列中的任務數(shù)。
3、運行原理:

剛開始都是在創(chuàng)建新的線程,達到核心線程數(shù)量5個后,新的任務進來后不再創(chuàng)建新的線程,而是將任務加入工作隊列,任務隊列到達上線5個后,新的任務又會創(chuàng)建新的普通線程,直到達到線程池最大的線程數(shù)量10個,后面的任務則根據(jù)配置的飽和策略來處理。我們這里沒有具體配置,使用的是默認的配置AbortPolicy:直接拋出異常。
當然,為了達到我需要的效果,上述線程處理的任務都是利用休眠導致線程沒有釋放?。?!

RejectedExecutionHandler:飽和策略

當隊列和線程池都滿了,說明線程池處于飽和狀態(tài),那么必須對新提交的任務采用一種特殊的策略來進行處理。這個策略默認配置是AbortPolicy,表示無法處理新的任務而拋出異常。JAVA提供了4中策略:

1、AbortPolicy:直接拋出異常
2、CallerRunsPolicy:只用調(diào)用所在的線程運行任務
3、DiscardOldestPolicy:丟棄隊列里最近的一個任務,并執(zhí)行當前任務。
4、DiscardPolicy:不處理,丟棄掉。

我們現(xiàn)在用第四種策略來處理上面的程序:

public static void main(String[] args)
{
    LinkedBlockingQueue<Runnable> queue =
      new LinkedBlockingQueue<Runnable>(3);
    RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, queue,handler);
    for (int i = 0; i < 9 ; i++)
    {
      threadPool.execute(
        new Thread(new ThreadPoolTest(), "Thread".concat(i + "")));
      System.out.println("線程池中活躍的線程數(shù): " + threadPool.getPoolSize());
      if (queue.size() > 0)
      {
        System.out.println("----------------隊列中阻塞的線程數(shù)" + queue.size());
      }
    }
    threadPool.shutdown();
}

執(zhí)行結果:

線程池中活躍的線程數(shù): 1
線程池中活躍的線程數(shù): 2
線程池中活躍的線程數(shù): 2
----------------隊列中阻塞的線程數(shù)1
線程池中活躍的線程數(shù): 2
----------------隊列中阻塞的線程數(shù)2
線程池中活躍的線程數(shù): 2
----------------隊列中阻塞的線程數(shù)3
線程池中活躍的線程數(shù): 3
----------------隊列中阻塞的線程數(shù)3
線程池中活躍的線程數(shù): 4
----------------隊列中阻塞的線程數(shù)3
線程池中活躍的線程數(shù): 5
----------------隊列中阻塞的線程數(shù)3
線程池中活躍的線程數(shù): 5
----------------隊列中阻塞的線程數(shù)3

這里采用了丟棄策略后,就沒有再拋出異常,而是直接丟棄。在某些重要的場景下,可以采用記錄日志或者存儲到數(shù)據(jù)庫中,而不應該直接丟棄。

設置策略有兩種方式:

1、

RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, queue,handler);

2、

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, queue);
threadPool.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());

更多java相關內(nèi)容感興趣的讀者可查看本站專題:《Java進程與線程操作技巧總結》、《Java數(shù)據(jù)結構與算法教程》、《Java操作DOM節(jié)點技巧總結》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》

希望本文所述對大家java程序設計有所幫助。

分享題目:JAVA線程池原理實例詳解
網(wǎng)址分享:http://jinyejixie.com/article28/gdpdcp.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供企業(yè)網(wǎng)站制作、移動網(wǎng)站建設、關鍵詞優(yōu)化、品牌網(wǎng)站建設外貿(mào)網(wǎng)站建設、ChatGPT

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化
大石桥市| 新丰县| 滨海县| 晋城| 新民市| 小金县| 拜城县| 九台市| 东台市| 岳西县| 扎鲁特旗| 和林格尔县| 平南县| 永丰县| 育儿| 镇安县| 永康市| 托克逊县| 台东市| 香格里拉县| 铜川市| 青浦区| 冕宁县| 巴楚县| 娄烦县| 阜宁县| 开封市| 宜州市| 呈贡县| 雅江县| 射洪县| 炉霍县| 鹤庆县| 年辖:市辖区| 梁山县| 吴江市| 卢龙县| 闻喜县| 定西市| 郴州市| 双鸭山市|