欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java concurrency線程池之線程池原理(二)_動力節(jié)點Java學院整理

 更新時間:2017年06月13日 10:34:12   作者:skywang12345  
這篇文章主要為大家詳細介紹了Java concurrency線程池之線程池原理第二篇,具有一定的參考價值,感興趣的小伙伴們可以參考一下

線程池示例

在分析線程池之前,先看一個簡單的線程池示例。

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;

public class ThreadPoolDemo1 {

  public static void main(String[] args) {
    // 創(chuàng)建一個可重用固定線程數(shù)的線程池
    ExecutorService pool = Executors.newFixedThreadPool(2);
    // 創(chuàng)建實現(xiàn)了Runnable接口對象,Thread對象當然也實現(xiàn)了Runnable接口
    Thread ta = new MyThread();
    Thread tb = new MyThread();
    Thread tc = new MyThread();
    Thread td = new MyThread();
    Thread te = new MyThread();
    // 將線程放入池中進行執(zhí)行
    pool.execute(ta);
    pool.execute(tb);
    pool.execute(tc);
    pool.execute(td);
    pool.execute(te);
    // 關(guān)閉線程池
    pool.shutdown();
  }
}

class MyThread extends Thread {

  @Override
  public void run() {
    System.out.println(Thread.currentThread().getName()+ " is running.");
  }
}

運行結(jié)果:

pool-1-thread-1 is running.
pool-1-thread-2 is running.
pool-1-thread-1 is running.
pool-1-thread-2 is running.
pool-1-thread-1 is running.

示例中,包括了線程池的創(chuàng)建,將任務添加到線程池中,關(guān)閉線程池這3個主要的步驟。稍后,我們會從這3個方面來分析ThreadPoolExecutor。 

線程池源碼分析

(一) 創(chuàng)建“線程池”

下面以newFixedThreadPool()介紹線程池的創(chuàng)建過程。

1. newFixedThreadPool()

newFixedThreadPool()在Executors.java中定義,源碼如下:

public static ExecutorService newFixedThreadPool(int nThreads) {
  return new ThreadPoolExecutor(nThreads, nThreads,
     0L, TimeUnit.MILLISECONDS,
     new LinkedBlockingQueue<Runnable>());
}

說明:newFixedThreadPool(int nThreads)的作用是創(chuàng)建一個線程池,線程池的容量是nThreads。
         newFixedThreadPool()在調(diào)用ThreadPoolExecutor()時,會傳遞一個LinkedBlockingQueue()對象,而LinkedBlockingQueue是單向鏈表實現(xiàn)的阻塞隊列。在線程池中,就是通過該阻塞隊列來實現(xiàn)"當線程池中任務數(shù)量超過允許的任務數(shù)量時,部分任務會阻塞等待"。
關(guān)于LinkedBlockingQueue的實現(xiàn)細節(jié),讀者可以參考"Java多線程系列--“JUC集合”08之 LinkedBlockingQueue"。 

2. ThreadPoolExecutor()

ThreadPoolExecutor()在ThreadPoolExecutor.java中定義,源碼如下:

public ThreadPoolExecutor(int corePoolSize,
             int maximumPoolSize,
             long keepAliveTime,
             TimeUnit unit,
             BlockingQueue<Runnable> workQueue) {
  this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
     Executors.defaultThreadFactory(), defaultHandler);
}

說明:該函數(shù)實際上是調(diào)用ThreadPoolExecutor的另外一個構(gòu)造函數(shù)。該函數(shù)的源碼如下:

public ThreadPoolExecutor(int corePoolSize,
             int maximumPoolSize,
             long keepAliveTime,
             TimeUnit unit,
             BlockingQueue<Runnable> workQueue,
             ThreadFactory threadFactory,
             RejectedExecutionHandler handler) {
  if (corePoolSize < 0 ||
    maximumPoolSize <= 0 ||
    maximumPoolSize < corePoolSize ||
    keepAliveTime < 0)
    throw new IllegalArgumentException();
  if (workQueue == null || threadFactory == null || handler == null)
    throw new NullPointerException();
  // 核心池大小
  this.corePoolSize = corePoolSize;
  // 最大池大小
  this.maximumPoolSize = maximumPoolSize;
  // 線程池的等待隊列
  this.workQueue = workQueue;
  this.keepAliveTime = unit.toNanos(keepAliveTime);
  // 線程工廠對象
  this.threadFactory = threadFactory;
  // 拒絕策略的句柄
  this.handler = handler;
}

說明:在ThreadPoolExecutor()的構(gòu)造函數(shù)中,進行的是初始化工作。
corePoolSize, maximumPoolSize, unit, keepAliveTime和workQueue這些變量的值是已知的,它們都是通過newFixedThreadPool()傳遞而來。下面看看threadFactory和handler對象。 

2.1 ThreadFactory

線程池中的ThreadFactory是一個線程工廠,線程池創(chuàng)建線程都是通過線程工廠對象(threadFactory)來完成的。
上面所說的threadFactory對象,是通過 Executors.defaultThreadFactory()返回的。Executors.java中的defaultThreadFactory()源碼如下:

public static ThreadFactory defaultThreadFactory() {
  return new DefaultThreadFactory();
}

defaultThreadFactory()返回DefaultThreadFactory對象。Executors.java中的DefaultThreadFactory()源碼如下:

 static class DefaultThreadFactory implements ThreadFactory {
  private static final AtomicInteger poolNumber = new AtomicInteger(1);
  private final ThreadGroup group;
  private final AtomicInteger threadNumber = new AtomicInteger(1);
  private final String namePrefix;

  DefaultThreadFactory() {
    SecurityManager s = System.getSecurityManager();
    group = (s != null) ? s.getThreadGroup() :
               Thread.currentThread().getThreadGroup();
    namePrefix = "pool-" +
           poolNumber.getAndIncrement() +
           "-thread-";
  }

  // 提供創(chuàng)建線程的API。
  public Thread newThread(Runnable r) {
    // 線程對應的任務是Runnable對象r
    Thread t = new Thread(group, r,
               namePrefix + threadNumber.getAndIncrement(),
               0);
    // 設為“非守護線程”
    if (t.isDaemon())
      t.setDaemon(false);
    // 將優(yōu)先級設為“Thread.NORM_PRIORITY”
    if (t.getPriority() != Thread.NORM_PRIORITY)
      t.setPriority(Thread.NORM_PRIORITY);
    return t;
  }
}

說明:ThreadFactory的作用就是提供創(chuàng)建線程的功能的線程工廠。
         它是通過newThread()提供創(chuàng)建線程功能的,下面簡單說說newThread()。newThread()創(chuàng)建的線程對應的任務是Runnable對象,它創(chuàng)建的線程都是“非守護線程”而且“線程優(yōu)先級都是Thread.NORM_PRIORITY”。 

2.2 RejectedExecutionHandler

handler是ThreadPoolExecutor中拒絕策略的處理句柄。所謂拒絕策略,是指將任務添加到線程池中時,線程池拒絕該任務所采取的相應策略。
線程池默認會采用的是defaultHandler策略,即AbortPolicy策略。在AbortPolicy策略中,線程池拒絕任務時會拋出異常!
defaultHandler的定義如下:

private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
AbortPolicy的源碼如下:

public static class AbortPolicy implements RejectedExecutionHandler {
  public AbortPolicy() { }

  // 拋出異常
  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    throw new RejectedExecutionException("Task " + r.toString() +
                       " rejected from " +
                       e.toString());
  }
}

(二) 添加任務到“線程池”

1. execute()

execute()定義在ThreadPoolExecutor.java中,源碼如下:

public void execute(Runnable command) {
  // 如果任務為null,則拋出異常。
  if (command == null)
    throw new NullPointerException();
  // 獲取ctl對應的int值。該int值保存了"線程池中任務的數(shù)量"和"線程池狀態(tài)"信息
  int c = ctl.get();
  // 當線程池中的任務數(shù)量 < "核心池大小"時,即線程池中少于corePoolSize個任務。
  // 則通過addWorker(command, true)新建一個線程,并將任務(command)添加到該線程中;然后,啟動該線程從而執(zhí)行任務。
  if (workerCountOf(c) < corePoolSize) {
    if (addWorker(command, true))
      return;
    c = ctl.get();
  }
  // 當線程池中的任務數(shù)量 >= "核心池大小"時,
  // 而且,"線程池處于允許狀態(tài)"時,則嘗試將任務添加到阻塞隊列中。
  if (isRunning(c) && workQueue.offer(command)) {
    // 再次確認“線程池狀態(tài)”,若線程池異常終止了,則刪除任務;然后通過reject()執(zhí)行相應的拒絕策略的內(nèi)容。
    int recheck = ctl.get();
    if (! isRunning(recheck) && remove(command))
      reject(command);
    // 否則,如果"線程池中任務數(shù)量"為0,則通過addWorker(null, false)嘗試新建一個線程,新建線程對應的任務為null。
    else if (workerCountOf(recheck) == 0)
      addWorker(null, false);
  }
  // 通過addWorker(command, false)新建一個線程,并將任務(command)添加到該線程中;然后,啟動該線程從而執(zhí)行任務。
  // 如果addWorker(command, false)執(zhí)行失敗,則通過reject()執(zhí)行相應的拒絕策略的內(nèi)容。
  else if (!addWorker(command, false))
    reject(command);
}

說明:execute()的作用是將任務添加到線程池中執(zhí)行。它會分為3種情況進行處理:
        情況1 -- 如果"線程池中任務數(shù)量" < "核心池大小"時,即線程池中少于corePoolSize個任務;此時就新建一個線程,并將該任務添加到線程中進行執(zhí)行。
        情況2 -- 如果"線程池中任務數(shù)量" >= "核心池大小",并且"線程池是允許狀態(tài)";此時,則將任務添加到阻塞隊列中阻塞等待。在該情況下,會再次確認"線程池的狀態(tài)",如果"第2次讀到的線程池狀態(tài)"和"第1次讀到的線程池狀態(tài)"不同,則從阻塞隊列中刪除該任務。
        情況3 -- 非以上兩種情況。在這種情況下,嘗試新建一個線程,并將該任務添加到線程中進行執(zhí)行。如果執(zhí)行失敗,則通過reject()拒絕該任務。

2. addWorker()

addWorker()的源碼如下:

private boolean addWorker(Runnable firstTask, boolean core) {
  retry:
  // 更新"線程池狀態(tài)和計數(shù)"標記,即更新ctl。
  for (;;) {
    // 獲取ctl對應的int值。該int值保存了"線程池中任務的數(shù)量"和"線程池狀態(tài)"信息
    int c = ctl.get();
    // 獲取線程池狀態(tài)。
    int rs = runStateOf(c);

    // 有效性檢查
    if (rs >= SHUTDOWN &&
      ! (rs == SHUTDOWN &&
        firstTask == null &&
        ! workQueue.isEmpty()))
      return false;

    for (;;) {
      // 獲取線程池中任務的數(shù)量。
      int wc = workerCountOf(c);
      // 如果"線程池中任務的數(shù)量"超過限制,則返回false。
      if (wc >= CAPACITY ||
        wc >= (core ? corePoolSize : maximumPoolSize))
        return false;
      // 通過CAS函數(shù)將c的值+1。操作失敗的話,則退出循環(huán)。
      if (compareAndIncrementWorkerCount(c))
        break retry;
      c = ctl.get(); // Re-read ctl
      // 檢查"線程池狀態(tài)",如果與之前的狀態(tài)不同,則從retry重新開始。
      if (runStateOf(c) != rs)
        continue retry;
      // else CAS failed due to workerCount change; retry inner loop
    }
  }

  boolean workerStarted = false;
  boolean workerAdded = false;
  Worker w = null;
  // 添加任務到線程池,并啟動任務所在的線程。
  try {
    final ReentrantLock mainLock = this.mainLock;
    // 新建Worker,并且指定firstTask為Worker的第一個任務。
    w = new Worker(firstTask);
    // 獲取Worker對應的線程。
    final Thread t = w.thread;
    if (t != null) {
      // 獲取鎖
      mainLock.lock();
      try {
        int c = ctl.get();
        int rs = runStateOf(c);

        // 再次確認"線程池狀態(tài)"
        if (rs < SHUTDOWN ||
          (rs == SHUTDOWN && firstTask == null)) {
          if (t.isAlive()) // precheck that t is startable
            throw new IllegalThreadStateException();
          // 將Worker對象(w)添加到"線程池的Worker集合(workers)"中
          workers.add(w);
          // 更新largestPoolSize
          int s = workers.size();
          if (s > largestPoolSize)
            largestPoolSize = s;
          workerAdded = true;
        }
      } finally {
        // 釋放鎖
        mainLock.unlock();
      }
      // 如果"成功將任務添加到線程池"中,則啟動任務所在的線程。 
      if (workerAdded) {
        t.start();
        workerStarted = true;
      }
    }
  } finally {
    if (! workerStarted)
      addWorkerFailed(w);
  }
  // 返回任務是否啟動。
  return workerStarted;
}

說明:
    addWorker(Runnable firstTask, boolean core) 的作用是將任務(firstTask)添加到線程池中,并啟動該任務。
    core為true的話,則以corePoolSize為界限,若"線程池中已有任務數(shù)量>=corePoolSize",則返回false;core為false的話,則以maximumPoolSize為界限,若"線程池中已有任務數(shù)量>=maximumPoolSize",則返回false。
    addWorker()會先通過for循環(huán)不斷嘗試更新ctl狀態(tài),ctl記錄了"線程池中任務數(shù)量和線程池狀態(tài)"。
    更新成功之后,再通過try模塊來將任務添加到線程池中,并啟動任務所在的線程。

    從addWorker()中,我們能清晰的發(fā)現(xiàn):線程池在添加任務時,會創(chuàng)建任務對應的Worker對象;而一個Workder對象包含一個Thread對象。(01) 通過將Worker對象添加到"線程的workers集合"中,從而實現(xiàn)將任務添加到線程池中。 (02) 通過啟動Worker對應的Thread線程,則執(zhí)行該任務。

 3. submit()

補充說明一點,submit()實際上也是通過調(diào)用execute()實現(xiàn)的,源碼如下:

public Future<?> submit(Runnable task) {
  if (task == null) throw new NullPointerException();
  RunnableFuture<Void> ftask = newTaskFor(task, null);
  execute(ftask);
  return ftask;
}

 (三) 關(guān)閉“線程池”

shutdown()的源碼如下:

public void shutdown() {
  final ReentrantLock mainLock = this.mainLock;
  // 獲取鎖
  mainLock.lock();
  try {
    // 檢查終止線程池的“線程”是否有權(quán)限。
    checkShutdownAccess();
    // 設置線程池的狀態(tài)為關(guān)閉狀態(tài)。
    advanceRunState(SHUTDOWN);
    // 中斷線程池中空閑的線程。
    interruptIdleWorkers();
    // 鉤子函數(shù),在ThreadPoolExecutor中沒有任何動作。
    onShutdown(); // hook for ScheduledThreadPoolExecutor
  } finally {
    // 釋放鎖
    mainLock.unlock();
  }
  // 嘗試終止線程池
  tryTerminate();
}

說明:shutdown()的作用是關(guān)閉線程池。

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • java使用RestTemplate封裝post請求方式

    java使用RestTemplate封裝post請求方式

    這篇文章主要介紹了java使用RestTemplate封裝post請求方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • java中樂觀鎖與悲觀鎖區(qū)別及使用場景分析

    java中樂觀鎖與悲觀鎖區(qū)別及使用場景分析

    本文主要介紹了java中樂觀鎖與悲觀鎖區(qū)別及使用場景分析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-08-08
  • java 2d畫圖示例分享(用java畫圖)

    java 2d畫圖示例分享(用java畫圖)

    這篇文章主要介紹了java 2D畫圖示例(用java畫圖),需要的朋友可以參考下
    2014-04-04
  • jvm原理之SystemGC源碼分析

    jvm原理之SystemGC源碼分析

    這篇文章主要介紹了jvm源碼分析之SystemGC的完全解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-01-01
  • Java窗體中關(guān)于默認布局管理器容易踩的坑及解決

    Java窗體中關(guān)于默認布局管理器容易踩的坑及解決

    這篇文章主要介紹了Java窗體中關(guān)于默認布局管理器容易踩的坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • Spring Framework常用面試題及答案匯總

    Spring Framework常用面試題及答案匯總

    這篇文章主要介紹了Spring Framework常用面試題及答案匯總,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-06-06
  • springboot集成sensitive-word實現(xiàn)敏感詞過濾的兩種方案

    springboot集成sensitive-word實現(xiàn)敏感詞過濾的兩種方案

    敏感詞過濾通常是指從文本中檢測并移除或替換掉被認為是不適當、冒犯性或違反特定社區(qū)準則的詞匯,這篇文章主要介紹了springboot集成sensitive-word實現(xiàn)敏感詞過濾,需要的朋友可以參考下
    2024-08-08
  • SpringBoot實現(xiàn)RAS+AES自動接口解密

    SpringBoot實現(xiàn)RAS+AES自動接口解密

    本文主要介紹了SpringBoot實現(xiàn)RAS+AES自動接口解密,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-03-03
  • MyBatis-Plus?updateById更新不了空字符串或null的解決方法

    MyBatis-Plus?updateById更新不了空字符串或null的解決方法

    本文主要介紹了MyBatis-Plus?updateById更新不了空字符串或null的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-03-03
  • Java特性之注解和異常?Throwable

    Java特性之注解和異常?Throwable

    這篇文章主要介紹了Java特性之注解和異常,注解是JDK1.5版本開始引入的一個特性,Throwable是Java語言中所有錯誤與異常的超類,文章圍繞主題展開更多的相關(guān)介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-06-06

最新評論