主頁 > 軟體設計 > 一文讀懂執行緒池的實作原理

一文讀懂執行緒池的實作原理

2021-04-21 11:11:08 軟體設計

歡迎大家關注我的微信公眾號【老周聊架構】,Java后端主流技術堆疊的原理、原始碼分析、架構以及各種互聯網高并發、高性能、高可用的解決方案,

一、前言

上個月底群里的一個好朋友向老周提出啥時候分享 ThreadPoolExecutor 決議大全,我說后面會提上日程;然后前些天有讀者也反饋說在面試中有被問到執行緒池,問我啥時候出一篇執行緒池相關的文章,今天老周就來安排一波執行緒池,現在很多公司都喜歡問執行緒池相關的面試題,為什么面試官這么熱衷于問執行緒池相關的面試題呢?因為這是多執行緒的基礎,ThreadPoolExecutor 的幾個重要引數你必須會知道設定以及什么場景選擇哪種 Executor 、執行緒池佇列的選擇以及相應的拒絕策略,

下面老周收集了幾個朋友提供的大廠關于執行緒池的面試題:

  • 執行緒池的使用場景
  • 執行緒池各個引數的含義,你平時用的什么佇列以及拒絕策略?
  • 程式中哪些地方用到了執行緒池,用執行緒池的好處有哪些?
  • 如何自己實作一個執行緒池
  • JDK 提供了哪些執行緒池的默認實作
  • 阿里巴巴 Java 開發手冊為啥不允許默認實作的執行緒池
  • 執行緒池里的引數你是怎么得出來的,根據什么算出來的?
  • 說說你自定義執行緒池里的作業流程

這里老周就不帶大家一個個對面試題進行分析了,這里對只講核心原理再結合動態調整執行緒池引數的實踐來幫助你對執行緒池有個清晰的認識,知道了原理再結合自己的實踐,那面試執行緒池也是得心應手了,那你有可能問,老周啊,我平時也沒用到執行緒池啊,用的也都是定義類繼承 Thread類 或者 定義類實作 Runnable 介面來實作多執行緒的啊,額,如果你是面的 Java 中高級開發,那你千萬不要這樣說,這會讓面試官一下覺得你不值中高級,如果你面的中高級還不知道執行緒池的話也沒關系,幸好你看到了老周這篇文章,還不算晚;如果你是已經用過執行緒池相關,那這篇文章也會讓你對執行緒池的原理更加清楚,在專案中應用也會得心應手,

二、執行緒池的概念

2.1 執行緒池是什么

執行緒池是一種執行緒使用模式,執行緒過多會帶來額外的開銷,其中包括創建銷毀執行緒的開銷、調度執行緒的開銷等等,同時也降低了計算機的整體性能,執行緒池維護多個執行緒,等待監督管理者分配可并發執行的任務,這種做法,一方面避免了處理任務時創建銷毀執行緒開銷的代價,另一方面避免了執行緒數量膨脹導致的過分調度問題,保證了對內核的充分利用,

2.2 使用執行緒池的好處

  • 降低資源消耗:通過池化技術重復利用已創建的執行緒,降低執行緒創建和銷毀造成的損耗,
  • 提高回應速度:任務到達時,無需等待執行緒創建即可立即執行,
  • 提高執行緒的可管理性:執行緒是稀缺資源,如果無限制創建,不僅會消耗系統資源,還會因為執行緒的不合理分布導致資源調度失衡,降低系統的穩定性,使用執行緒池可以進行統一的分配、調優和監控,
  • 提供更多更強大的功能:執行緒池具備可拓展性,允許開發人員向其中增加更多的功能,比如延時定時執行緒池 ScheduledThreadPoolExecutor,就允許任務延期執行或定期執行,

2.3、ThreadPoolExecutor 的核心引數

網上說的天花亂墜的,也不如直接看 Doug Lea 大佬原始碼的注釋來的更加貼切些,
在這里插入圖片描述

  • corePoolSize:the number of threads to keep in the pool, even if they are idle, unless {@code allowCoreThreadTimeOut} is set
    核心執行緒數:執行緒池中保留的執行緒數,即使它們是空閑的,除非設定 allowCoreThreadTimeOut,

  • maximumPoolSize:the maximum number of threads to allow in the pool
    最大執行緒數:執行緒池中允許的最大執行緒數

  • keepAliveTime:when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.
    執行緒空閑時間:如果經過 keepAliveTime 時間后,超過核心執行緒數的執行緒還沒有接受到新的任務,那就回收,

  • unit:the time unit for the {@code keepAliveTime} argument
    單位:keepAliveTime 的時間單位

  • workQueue:the queue to use for holding tasks before they are executed. This queue will hold only the {@code Runnable} tasks submitted by the {@code execute} method.
    存放待執行任務的佇列:當提交的任務數超過核心執行緒數后,再提交的任務就存放在這里,它僅僅用來存放被 execute 方法提交的 Runnable 任務,(這里不要再翻譯成作業佇列了好嗎)

  • threadFactory:the factory to use when the executor creates a new thread
    執行緒工廠:執行程式創建新執行緒時使用的工廠,比如我們專案中自定義的執行緒工廠,排查問題的時候,根據執行緒工廠的名稱就知道這個執行緒來自哪里,很快的定位出問題,

  • handler :the handler to use when execution is blocked because the thread bounds and queue capacities are reached
    拒絕策略:當佇列里面放滿了任務、最大執行緒數的執行緒都在作業時,這時繼續提交的任務執行緒池就處理不了,應該執行怎么樣的拒絕策略,

三、執行緒池的實作原理

本文描述執行緒池是 JDK 8 中提供的 ThreadPoolExecutor 類,那我們就從 ThreadPoolExecutor 類來看下它的 UML 依賴關系,

3.1 總體設計

在這里插入圖片描述

  • 藍色實線:繼承關系
  • 綠色虛線:介面實作關系
  • 綠色實作:介面繼承關系

ThreadPoolExecutor 實作的頂層介面是 Executor,頂層介面只提供了void execute(Runnable command); 這么一個方法,Executor 提供的是一種思想:將任務提交和任務執行進行解耦,用戶無需關注如何創建執行緒,如何調度執行緒來執行任務,用戶只需提供 Runnable 物件,將任務的運行邏輯提交到執行器(Executor)中,由 Executor 框架完成執行緒的調配和任務的執行部分,

ExecutorService 介面增加了一些能力:

  • 擴充執行任務的能力,補充可以為一個或一批異步任務生成 Future 的方法;
  • 提供了管控執行緒池的方法,比如停止執行緒池的運行,

AbstractExecutorService 則是上層的抽象類,將執行任務的流程串聯了起來,保證下層的實作只需關注一個執行任務的方法即可,最下層的實作類 ThreadPoolExecutor 實作最復雜的運行部分,ThreadPoolExecutor 將會一方面維護自身的生命周期,另一方面同時管理執行緒和任務,使兩者良好的結合從而執行并行任務,

我們來看下 ThreadPoolExecutor 的運行流程:

在這里插入圖片描述
執行緒池在內部實際上構建了一個生產者消費者模型,將執行緒和任務兩者解耦,并不直接關聯,從而良好的緩沖任務,復用執行緒,執行緒池的運行主要分成兩部分:任務管理、執行緒管理,

任務管理部分充當生產者的角色,當任務提交后,執行緒池會判斷該任務后續的流轉:

  • 直接申請執行緒執行該任務
  • 緩沖到佇列中等待執行緒執行
  • 拒絕該任務

執行緒管理部分充當消費者的角色,它們被統一維護在執行緒池內,根據任務請求進行執行緒的分配,當執行緒執行完任務后則會繼續獲取新的任務去執行,最終當執行緒獲取不到任務的時候,執行緒就會被回收,

下面就從以下三個核心機制來詳細講解執行緒池運行機制:

  • 執行緒池如何維護自身狀態
  • 執行緒池如何管理任務
  • 執行緒池如何管理執行緒

3.2 執行緒池如何維護自身狀態

執行緒池運行的狀態,并不是用戶顯式設定的,而是伴隨著執行緒池的運行,由內部來維護,執行緒池內部使用一個變數維護兩個值:運行狀態(runState)和執行緒數量(workerCount),
在這里插入圖片描述

ctl 這個 AtomicInteger 型別,是對執行緒池的運行狀態和執行緒池中有效執行緒的數量進行控制的一個欄位, 它同時包含兩部分的資訊:執行緒池的運行狀態 (runState) 和執行緒池內有效執行緒的數量 (workerCount),高 3 位保存 runState,低 29 位保存 workerCount,兩個變數之間互不干擾,用一個變數去存盤兩個值,可避免在做相關決策時,出現不一致的情況,不必為了維護兩者的一致,而占用鎖資源,通過閱讀執行緒池源代碼也可以發現,經常出現要同時判斷執行緒池運行狀態和執行緒數量的情況,執行緒池也提供了若干方法去供用戶獲得執行緒池當前的運行狀態、執行緒個數,這里都使用的是位運算的方式,相比于基本運算,速度也會快很多,

關于內部封裝的獲取生命周期狀態、獲取執行緒池執行緒數量的計算方法如下代碼:
在這里插入圖片描述
哇,Doug Lea 大佬簡直了,設計的真好,老周等等我,這里怎么設計的就好了?CAPACITY 這里是多少呀?

不著急,老周這就帶你來分析分析為什么一個整型變數既可以保存運行狀態,又可以保存執行緒數量?

首先,我們知道 Java 中 1 個整型占 4 個位元組,也就是 32 位,所以 1 個整型有 32 位,

所以整型 1 用二進制表示就是:0000 0000 0000 0000 0000 0000 0000 0001

整型 -1 用二進制表示就是:1111 1111 1111 1111 1111 1111 1111 1111 (這個是補碼,這個忘了的話那得去復習下原碼、反碼、補碼等計算機基礎知識了,)

在 ThreadPoolExecutor,整型中 32 位的前 3 位用來表示執行緒池狀態,后 29 位表示執行緒池中有效的執行緒數,

在這里插入圖片描述
這里你有可能問了,老周啊,CAPACITY = (1 << 29) - 1 怎么就得到 0001 1111 1111 1111 1111 1111 1111 1111,

好吧,老周就帶你分析下 CAPACITY 怎么來的,下面的那些狀態大家也可以自己去分析下哈,

我們先來看 1 << 29,首先看 1 的二進制代表 0000 0000 0000 0000 0000 0000 0000 0001,

然后 0000 0000 0000 0000 0000 0000 0000 0001 向左移 29 位,得到 0010 0000 0000 0000 0000 0000 0000 0000,

最后將 0010 0000 0000 0000 0000 0000 0000 0000 減 1 得到 0001 1111 1111 1111 1111 1111 1111 1111,

我們下面再來了解下 ThreadPoolExecutor 所定義的狀態,這些狀態都和執行緒的執行密切相關:

在這里插入圖片描述

  • RUNNING:能接受新提交的任務,并且也能處理阻塞佇列中的任務,
  • SHUTDOWN:指呼叫了 shutdown() 方法,不再接受新提交的任務,但卻可以繼續處理阻塞佇列中已保存的任務,
  • STOP:指呼叫了 shutdownNow() 方法,不再接受新提交的任務,同時拋棄阻塞佇列里的所有任務并中斷所有正在執行任務,
  • TIDYING: 所有任務都執行完畢,workerCount 有效執行緒數為 0,
  • TERMINATED:終止狀態,當執行 terminated() 后會更新為這個狀態,

在這里插入圖片描述

3.3 執行緒池如何管理任務

3.3.1 任務調度

任務調度是執行緒池的主要入口,當用戶提交了一個任務,接下來這個任務將如何執行都是由這個階段決定的,了解這部分就相當于了解了執行緒池的核心運行機制,

首先,所有任務的調度都是由 execute 方法完成的,比如我們業務代碼中
threadPool.execute(new Job());

這部分完成的作業是:檢查現在執行緒池的運行狀態、運行執行緒數、運行策略,決定接下來執行的流程,是直接申請執行緒執行,或是緩沖到佇列中執行,亦或是直接拒絕該任務,其執行程序如下:

  • 首先檢測執行緒池運行狀態,如果不是 RUNNING,則直接拒絕,執行緒池要保證在 RUNNING 的狀態下執行任務,
  • 如果 workerCount < corePoolSize,則創建并啟動一個執行緒來執行新提交的任務,
  • 如果 workerCount >= corePoolSize,且執行緒池內的阻塞佇列未滿,則將任務添加到該阻塞佇列中,
  • 如果 workerCount >= corePoolSize && workerCount < maximumPoolSize,且執行緒池內的阻塞佇列已滿,則創建并啟動一個執行緒來執行新提交的任務,
  • 如果 workerCount >= maximumPoolSize,并且執行緒池內的阻塞佇列已滿,則根據拒絕策略來處理該任務,默認的處理方式是直接拋例外,

執行流程圖如下:

在這里插入圖片描述
3.3.2 待執行任務的佇列

待執行任務的佇列是執行緒池能夠管理任務的核心部分,執行緒池的本質是對任務和執行緒的管理,而做到這一點最關鍵的思想就是將任務和執行緒兩者解耦,不讓兩者直接關聯,才可以做后續的分配作業,執行緒池中是以生產者消費者模式,通過一個阻塞佇列來實作的,阻塞佇列快取任務,作業執行緒從阻塞佇列中獲取任務,

阻塞佇列(BlockingQueue)是一個支持兩個附加操作的佇列,

這兩個附加的操作是:

  • 在佇列為空時,獲取元素的執行緒會等待佇列變為非空,
  • 當佇列滿時,存盤元素的執行緒會等待佇列可用,

阻塞佇列常用于生產者和消費者的場景,生產者是往佇列里添加元素的執行緒,消費者是從佇列里拿元素的執行緒,阻塞佇列就是生產者存放元素的容器,而消費者也只從容器里拿元素,

下圖中展示了 Thread1 往阻塞佇列中添加元素,而執行緒 Thread2 從阻塞佇列中移除元素:

在這里插入圖片描述
使用不同的佇列可以實作不一樣的任務存取策略,我們下面來看下阻塞佇列的成員:

在這里插入圖片描述
3.3.3 任務申請

從勺ò干知,任務的執行有兩種可能:

  • 一種是任務直接由新創建的執行緒執行
  • 另一種是執行緒從任務佇列中獲取任務然后執行,執行完任務的空閑執行緒會再次去從佇列中申請任務再去執行,

第一種情況僅出現在執行緒初始創建的時候,第二種是執行緒獲取任務絕大多數的情況,

執行緒需要從待執行任務的佇列中不斷地取任務執行,幫助執行緒從阻塞佇列中獲取任務,實作執行緒管理模塊和任務管理模塊之間的通信,

這部分策略由 getTask 方法實作,我們來看下 getTask 方法的代碼,
在這里插入圖片描述
getTask 方法在阻塞佇列中有待執行的任務時會從佇列中彈出一個任務并回傳,如果阻塞佇列為空,那么就會阻塞等待新的任務提交到佇列中直到超時(在一些配置下會一直等待而不超時),如果在超時之前獲取到了新的任務,那么就會將這個任務作為回傳值回傳,所以一般 getTask 方法是不會回傳 null 的,只會阻塞等待下一個任務并在之后將這個新任務作為回傳值回傳,

當 getTask 方法回傳 null 時會導致當前 Worker 退出,當前執行緒被銷毀,在以下情況下 getTask 方法才會回傳 null:

  • 當前執行緒池中的執行緒數超過了最大執行緒數,這是因為運行時通過呼叫 setMaximumPoolSize 修改了最大執行緒數而導致的結果;
  • 執行緒池處于 STOP 狀態,這種情況下所有執行緒都應該被立即回收銷毀;
  • 執行緒池處于 SHUTDOWN 狀態,且阻塞佇列為空,這種情況下已經不會有新的任務被提交到阻塞佇列中了,所以執行緒應該被銷毀;
  • 執行緒可以被超時回收的情況下等待新任務超時,執行緒被超時回收一般有以下兩種情況:
    • 允許核心執行緒超時(執行緒池配置)的情況下執行緒等待任務超時
    • 超出核心執行緒數部分的執行緒等待任務超時

3.3.4 任務拒絕

任務拒絕模塊是執行緒池的保護部分,執行緒池有一個最大的容量,當執行緒池的任務快取佇列已滿,并且執行緒池中的執行緒數目達到 maximumPoolSize 時,就需要拒絕掉該任務,采取任務拒絕策略,保護執行緒池,

拒絕策略是一個介面,其設計如下:

在這里插入圖片描述
用戶可以通過實作這個介面去定制拒絕策略,也可以選擇 JDK 提供的四種已有拒絕策略,其特點如下:
在這里插入圖片描述

3.4 執行緒池如何管理執行緒

3.4.1 Worker執行緒

執行緒池為了掌握執行緒的狀態并維護執行緒的生命周期,設計了執行緒池內的作業執行緒 Worker,我們來看一下它的代碼:

在這里插入圖片描述
Worker 這個作業執行緒,實作了 Runnable 介面,并持有一個執行緒thread,一個初始化的任務firstTask,thread 是在呼叫構造方法時通過 ThreadFactory 來創建的執行緒,可以用來執行任務;

firstTask 用它來保存傳入的第一個任務,這個任務可以有也可以為 null,如果這個值是非空的,那么執行緒就會在啟動初期立即執行這個任務,也就對應核心執行緒創建時的情況;如果這個值是空的,那么就需要創建一個執行緒去執行任務串列(workQueue)中的任務,也就是非核心執行緒的創建,

3.4.1.1 AQS 作用

Worker 繼承了 AbstractQueuedSynchronizer,主要目的有兩個:

  • 將鎖的粒度細化到每個 Worker
    如果多個 Worker 使用同一個鎖,那么一個 Worker Running 持有鎖的時候,其他 Worker 就無法執行,這顯然是不合理的,

  • 直接使用 CAS 獲取,避免阻塞,
    如果這個鎖使用阻塞獲取,那么在多 Worker 的情況下執行 shutDown,如果這個 Worker 此時正在 Running 無法獲取到鎖,那么執行 shutDown() 執行緒就會阻塞住了,顯然是不合理的,

3.4.1.2 Runnable 作用

Worker 還實作了 Runnable,它有兩個屬性 thead、firstTask,

firstTask 用它來保存傳入的第一個任務,這個任務可以有也可以為 null,

  • 如果這個值是非空的,那么執行緒就會在啟動初期立即執行這個任務,也就對應核心執行緒創建時的情況,
  • 如果這個值是 null,那么就需要創建一個執行緒去執行任務串列(workQueue)中的任務,也就是非核心執行緒的創建,

根據整體流程:

執行緒池呼叫 execute —> 創建 Worker(設定屬性thead、firstTask)—> worker.thread.start() —> 實際上呼叫的是 worker.run() —> 執行緒池的 runWorker(worker) —> worker.firstTask.run() (如果 firstTask 為 null 就從等待佇列中拉取一個),

Worker 執行任務的模型如下圖所示:
在這里插入圖片描述
3.4.2 Worker 執行緒增加

增加執行緒是通過執行緒池中的 addWorker 方法,該方法的功能就是增加一個執行緒,該方法不考慮執行緒池是在哪個階段增加的該執行緒,這個分配執行緒的策略是在上個步驟完成的,該步驟僅僅完成增加執行緒,并使它運行,最后回傳是否成功這個結果,

addWorker 方法有兩個引數:firstTask、core,

  • firstTask 引數用于指定新增的執行緒執行的第一個任務,該引數可以為空;
  • core 引數為 true 表示在新增執行緒時會判斷當前活動執行緒數是否少于 corePoolSize,false 表示新增執行緒前需要判斷當前活動執行緒數是否少于 maximumPoolSize,

我們來看一下 addWorker 的原始碼:

在這里插入圖片描述
原始碼看著是不是挺費勁的?沒關系,再看一張執行流程圖加深下映象,

在這里插入圖片描述

3.4.3 Worker 執行緒執行任務

Worker 中的執行緒 start 的時候,呼叫 Worker 本身 run 方法,這個 run 方法呼叫外部類ThreadPoolExecutor 的 runWorker 方法,直接看 runWorker 方法的原始碼:

在這里插入圖片描述
執行流程如下:

  • while 回圈不斷地通過 getTask() 方法獲取任務
  • getTask() 方法從阻塞佇列中取任務
  • 如果執行緒池正在停止,那么要保證當前執行緒是中斷狀態,否則要保證當前執行緒不是中斷狀態,
  • 執行任務
  • 如果 getTask 結果為 null 則跳出回圈,執行 processWorkerExit() 方法,銷毀執行緒,

在這里插入圖片描述
3.4.4 Worker 執行緒回收

執行緒池中執行緒的銷毀依賴 JVM 自動的回收,執行緒池做的作業是根據當前執行緒池的狀態維護一定數量的執行緒參考,防止這部分執行緒被 JVM 回收,當執行緒池決定哪些執行緒需要回收時,只需要將其參考消除即可,Worker 被創建出來后,就會不斷地進行輪詢,然后獲取任務去執行,核心執行緒可以無限等待獲取任務,非核心執行緒要限時獲取任務,當 Worker 無法獲取到任務,也就是獲取的任務為空時,回圈會結束,Worker 會主動消除自身在執行緒池內的參考,

執行緒回收的作業是在 processWorkerExit 方法完成的,

在這里插入圖片描述
在回收 Worker 的時候執行緒池會嘗試結束自己的運行,tryTerminate 方法:

在這里插入圖片描述
3.4.4 Worker 執行緒關閉

說到執行緒關閉,我們就不得不來說說 shutdown 方法和 shutdownNow 方法,

3.4.4.1 shutdown

在這里插入圖片描述
interruptIdleWorkers 方法,注意,這個方法打斷的是閑置 Worker,打斷閑置 Worker 之后,getTask 方法會回傳 null,然后 Worker 會被回收,那什么是閑置 Worker 呢?

閑置 Worker 是這樣解釋的:Worker 運行的時候會去阻塞佇列拿資料(getTask方法),拿的時候如果沒有設定超時時間,那么會一直阻塞等待阻塞佇列進資料,這樣的 Worker 就被稱為閑置 Worker,由于 Worker 也是一個 AQS,在 runWorker 方法里會有一對 lock 和 unlock 操作,這對 lock 操作是為了確保 Worker 不是一個閑置 Worker,

所以 Worker 被設計成一個 AQS 是為了根據 Worker 的鎖來判斷是否是閑置執行緒,是否可以被強制中斷,

下面我們看下 interruptIdleWorkers 方法:

在這里插入圖片描述
3.4.4.2 shutdownNow

shutdown 方法將執行緒池狀態改成 SHUTDOWN,執行緒池還能繼續處理阻塞佇列里的任務,并且會回收一些閑置的 Worker,但是 shutdownNow 方法不一樣,它會把執行緒池狀態改成 STOP 狀態,這樣不會處理阻塞佇列里的任務,也不會處理新的任務,

在這里插入圖片描述
shutdownNow 的中斷和 shutdown 方法不一樣,呼叫的是 interruptWorkers 方法:
在這里插入圖片描述
3.4.4.3 Worker 執行緒關閉小結

shutdown 方法會更新狀態到 SHUTDOWN,不會影響阻塞佇列里任務的執行,但是不會執行新進來的任務,同時也會回收閑置的 Worker,閑置 Worker 的定義上面已經說過了,

shutdownNow 方法會更新狀態到 STOP,會影響阻塞佇列的任務執行,也不會執行新進來的任務,同時會回收所有的 Worker,

?這里老周就不寫總結了,每塊都分析的很清楚了,相信大家看完這篇文章?,心里也有了自己想要的答案,


歡迎大家關注我的公眾號【老周聊架構】,Java后端主流技術堆疊的原理、原始碼分析、架構以及各種互聯網高并發、高性能、高可用的解決方案,

在這里插入圖片描述
喜歡的話,一鍵三連走一波,

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/278450.html

標籤:其他

上一篇:網路知識點小記

下一篇:Python和pycharm安裝程序詳解,內含安裝包小白入門必備【2021年最新】

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more