主頁 > 軟體設計 > 深入理解JVM虛擬機讀書筆記——記憶體模型與執行緒

深入理解JVM虛擬機讀書筆記——記憶體模型與執行緒

2021-09-02 08:42:48 軟體設計

:本文參考自周志明老師的著作《深入理解Java虛擬機(第3版)》,相關電子書可以關注WX公眾號,回復 001 獲取,

1. Java記憶體模型

JMM概述:

Java 記憶體模型指的是 JMM,而不是運行時資料區哦~

  • Java 語言為了保證并發編程中可以滿足原子性、可見性及有序性,于是推出了一個概念就是 JMM 記憶體模型,

  • JMM 記憶體模型,目的是為了在多執行緒條件下,使用共享記憶體進行資料通信時,通過對多執行緒程式讀操作、寫操作行為規范約束,來盡量避免多次記憶體資料讀取不一致、編譯器對代碼指令重排序、處理器對代碼亂序執行等帶來的問題,

    • JMM 記憶體模型解決并發問題主要采用兩種方式:限制處理器優化使用記憶體屏障
    • JMM 記憶體模型將記憶體主要劃分為主記憶體作業記憶體兩種,規定 所有的變數都存盤在主記憶體中,每條執行緒都擁有自己的作業記憶體,執行緒的作業記憶體中保存了該執行緒所需要用到的變數在主記憶體中的副本拷貝,執行緒對變數的所有操作都必須在作業記憶體中進行,而不能直接讀、寫主記憶體
    • 不同的執行緒之間也無法直接訪問對方作業記憶體中的變數,執行緒間變數的傳遞均需要執行緒自己的作業記憶體和主存之間進行資料互動,

    如圖所示:

JMM 記憶體模型作業記憶體、主記憶體和 JVM 記憶體有什么關系?

JMM 記憶體模型中,作業記憶體和主記憶體其實跟JVM記憶體的劃分是在不同層次上進行的,是自己的一套抽象概念,大概可以理解為,主記憶體對應的是 Java 堆中的物件實體部分,而作業記憶體對應的則是堆疊中的部磁區域,

1.1 主記憶體與作業記憶體

Java記憶體模型規定了所有的變數都存盤在主記憶體(Main Memory)中,每條執行緒還有自己的作業記憶體(Working Memory,可與前面講的處理器高速快取類比),執行緒的作業記憶體中保存了被該執行緒使用的變數的主記憶體副本[2],執行緒對變數的所有操作(讀取、賦值等)都必須在作業記憶體中進行,而不能直接讀寫主記憶體中的資料[3],不同的執行緒之間也無法直接訪問對方作業記憶體中的變數,執行緒間變數值的傳遞均需要通過主記憶體來完成,

執行緒、主記憶體、作業記憶體三者的互動關系如下圖所示:

這里所講的主記憶體、作業記憶體與第2章所講的Java記憶體區域中的Java堆、堆疊、方法區等并不是同一個層次的對記憶體的劃分,這兩者基本上是沒有任何關系的,如果兩者一定要勉強對應起來,那么從變數、主記憶體、作業記憶體的定義來看,主記憶體主要對應于Java堆中的物件實體資料部分,而作業記憶體則對應于虛擬機堆疊中的部磁區域,從更基礎的層次上說,主記憶體直接對應于物理硬體的記憶體,而為了獲取更好的運行速度,虛擬機(或者是硬體、作業系統本身的優化措施)可能會讓作業記憶體優先存盤于暫存器和高速快取中,因為程式運行時主要訪問的是作業記憶體,

1.2 記憶體間互動操作

關于主記憶體與作業記憶體之間具體的互動協議,即一個變數如何從主記憶體拷貝到作業記憶體、如何從作業記憶體同步回主記憶體這一類的實作細節,Java記憶體模型中定義了8 個操作來完成主記憶體作業記憶體的互動操作:

  • ① 首先是從 lock 加鎖開始,作用于主記憶體的變數,把一個變數標識為一條執行緒獨占的狀態;
  • read 讀取,作用于主記憶體變數,將一個變數的值從主記憶體讀取到作業記憶體中;
  • load 加載,作用于作業記憶體的變數,把 read 讀取到的值加載到作業記憶體的變數副本中;
  • use 使用,作用于作業記憶體的變數,把作業記憶體中變數的值傳遞給執行引擎使用,每當虛擬機遇到一個需要使用變數值的位元組碼指令時將會執行這個操作;
  • assign 賦值,作用于作業記憶體的變數,把從執行引擎接收到的值賦值給作業記憶體的變數,每當虛擬機遇到一個需要使用變數值的位元組碼指令時將會執行這個操作;
  • store 存盤,作用于作業記憶體的變數,把作業記憶體中變數的值傳送回主記憶體中,以便隨后的 write 的操作;
  • write 寫入,作用于主記憶體的變數,把 store 得到的值放入主記憶體的變數中;
  • ⑧ 最后是 unlock 解鎖,把主記憶體中處于鎖定狀態的變數釋放出來,流程到這一步就結束了,

如圖所示:

JMM 基本可以說是圍繞著在并發中如何處理這三個特性而建立起來的,也就是原子性、可見性、以及有序性,

如果要把一個變數從主記憶體拷貝到作業記憶體,那就要按順序執行read和load操作,如果要把變數從作業記憶體同步回主記憶體,就要按順序執行store和write操作,注意,Java記憶體模型只要求上述兩個操作必須按順序執行,但不要求是連續執行,也就是說read與load之間、store與write之間是可插入其他指令的,如對主記憶體中的變數a、b進行訪問時,一種可能出現的順序是read a、read b、load b、load a,除此之外,Java記憶體模型還規定了在執行上述8種基本操作時必須滿足如下規則:

  • 不允許read和load、store和write操作之一單獨出現,即不允許一個變數從主記憶體讀取了但作業記憶體不接受,或者作業記憶體發起回寫了但主記憶體不接受的情況出現,
  • 不允許一個執行緒丟棄它最近的assign操作,即變數在作業記憶體中改變了之后必須把該變化同步回主記憶體,
  • 不允許一個執行緒無原因地(沒有發生過任何assign操作)把資料從執行緒的作業記憶體同步回主記憶體中,
  • 一個新的變數只能在主記憶體中“誕生”,不允許在作業記憶體中直接使用一個未被初始化(load或assign)的變數,換句話說就是對一個變數實施use、store操作之前,必須先執行assign和load操作,
  • 一個變數在同一個時刻只允許一條執行緒對其進行lock操作,但lock操作可以被同一條執行緒重復執行多次,多次執行lock后,只有執行相同次數的unlock操作,變數才會被解鎖,
  • 如果對一個變數執行lock操作,那將會清空作業記憶體中此變數的值,在執行引擎使用這個變數前,需要重新執行load或assign操作以初始化變數的值,
  • 如果一個變數事先沒有被lock操作鎖定,那就不允許對它執行unlock操作,也不允許去unlock一個被其他執行緒鎖定的變數,
  • 對一個變數執行unlock操作之前,必須先把此變數同步回主記憶體中(執行store、write操作),

這8種記憶體訪問操作以及上述規則限定,再加上后面介紹的專門針對volatile的一些特殊規定,就已經能準確地描述出Java程式中哪些記憶體訪問操作在并發下才是安全的,

1.3 對于volatile型變數的特殊規則

關鍵字 volatile 可以說是Java虛擬機提供的最輕量級的同步機制,當一個變數被定義成 volatile 之后,它將具備兩項特性:

  • 第一項是保證變數對所有執行緒的可見性,這里的“可見性”是指當一條執行緒修改了這個變數的值,新值對于其他執行緒來說是可以立即得知的,而普通變數并不能做到這一點,普通變數的值在執行緒間傳遞時均需要通過主記憶體來完成,比如,執行緒A修改一個普通變數的值,然后向主記憶體進行回寫,另外一條執行緒B在執行緒A回寫完成了之后再對主記憶體進行讀取操作,新變數值才會對執行緒B可見,
  • 使用volatile變數的第二個語意是禁止指令重排序優化,普通的變數僅會保證在該方法的執行程序中所有依賴賦值結果的地方都能獲取到正確的結果,而不能保證變數賦值操作的順序與程式代碼中的執行順序一致,

1.3.1 volatile保證可見性的使用場景

退不出的回圈:

先來看一個現象,main 執行緒對 run 變數的修改對于 t 執行緒不可見,導致了 t 執行緒無法停止:

static boolean run = true;

public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(()->{
        while(run){
        // ....
        }
    });
 
    t.start();
    
    Thread.sleep(1000);
    
    run = false; // 執行緒t不會如預想的停下來
}

首先 t 執行緒運行,然后過一秒,主執行緒設定 run 的值為 false,想讓 t 執行緒停止下來,但是 t 執行緒并沒有停!

為什么呢?來圖解分析一下:

  1. 初始狀態, t 執行緒剛開始從主記憶體讀取了 run 的值到作業記憶體,

    在這里插入圖片描述

  2. .因為 t 執行緒要頻繁從主記憶體中讀取 run 的值,JIT 編譯器會將 run 的值快取至自己作業記憶體中的高 速快取中,減少對主存中 run 的訪問,提高效率

    在這里插入圖片描述

  3. 1 秒之后,main 執行緒修改了 run 的值,并同步至主存,而 t 是從自己作業記憶體中的高速快取中讀 取這個變數的值,結果永遠是舊值

    在這里插入圖片描述

解決方法:

volatile(關鍵字):

它可以用來修飾成員變數和靜態成員變數,他可以避免執行緒從自己的作業快取中查找變數的值,必須到 主存中獲取它的值,執行緒操作 volatile 變數都是直接操作主存,

 public static volatile boolean run = true; // 保證記憶體的可見性

volatile保證可見性

前面例子體現的實際就是可見性,它保證的是在多個執行緒之間,一個執行緒對 volatile 變數的修改對另一 個執行緒可見, 不能保證原子性,僅用在一個寫執行緒,多個讀執行緒的情況: 上例從位元組碼理解是這樣的:

getstatic run // 執行緒 t 獲取 run true
getstatic run // 執行緒 t 獲取 run true
getstatic run // 執行緒 t 獲取 run true
getstatic run // 執行緒 t 獲取 run true
putstatic run // 執行緒 main 修改 run 為 false, 僅此一次
getstatic run // 執行緒 t 獲取 run false

比較一下之前我們將執行緒安全時舉的例子:兩個執行緒一個i++ 一個 i-- ,只能保證看到最新值,不能解 決指令交錯

// 假設i的初始值為0
getstatic i 	// 執行緒1-獲取靜態變數i的值 執行緒內i=0
getstatic i 	// 執行緒2-獲取靜態變數i的值 執行緒內i=0
iconst_1 		// 執行緒1-準備常量1
iadd 			// 執行緒1-自增 執行緒內i=1
putstatic i 	// 執行緒1-將修改后的值存入靜態變數i 靜態變數i=1
iconst_1 		// 執行緒2-準備常量1
isub 			// 執行緒2-自減 執行緒內i=-1
putstatic i 	// 執行緒2-將修改后的值存入靜態變數i 靜態變數i

注意

synchronized 陳述句塊既可以保證代碼塊的原子性,也同時保證代碼塊內變數的可見性,但缺點是synchronized是屬于重量級操作,性能相對更低

如果在前面示例的死回圈中加入 System.out.println() 會發現即使不加 volatile 修飾符,執行緒 t 也 能正確看到對 run 變數的修改了,想一想為什么?(因為println()中有synchronized關鍵字加鎖,可以保證原子性與可見性,它是 PrintStream 類的方法 )

1.3.2 volatile保證有序性的使用場景

詭異的結果(指令重排):

首先看一個例子:

// 可以重排的例子 
int a = 10; 
int b = 20; 
System.out.println( a + b );

// 不能重排的例子 
int a = 10;
int b = a - 5;

指令重排簡單來說可以,在程式結果不受影響的前提下,可以調整指令陳述句執行順序,多執行緒下指令重排會影響正確性,

多執行緒下指令重排問題:

再分析下面的代碼:

int num = 0;

// volatile 修飾的變數,可以禁用指令重排 volatile boolean ready = false; 可以防止變數之前的代碼被重排序
boolean ready = false; 
// 執行緒1 執行此方法
public void actor1(I_Result r) {
 if(ready) {
 	r.r1 = num + num;
 } 
 else {
 	r.r1 = 1;
 }
}
// 執行緒2 執行此方法
public void actor2(I_Result r) {
 num = 2;
 ready = true;
}

I_Result 是一個物件,有一個屬性 r1 用來保存結果,問,可能的結果有幾種?

在多執行緒環境下,以上的代碼 r1 的值有三種情況:

  • 情況1:執行緒1 先執行,這時 ready = false,所以進入 else 分支結果為 1

  • 情況2:執行緒2 先執行 num = 2,但沒來得及執行ready = true,執行緒1 執行,還是進入 else 分支,結 果為1

  • 情況3:執行緒 2 先執行,但是發送了指令重排,num = 2ready = true 這兩行代碼語序發生裝換,

    ready = true; // 前
    num = 2; // 后
    

    然后執行 ready = true 后,執行緒 1 運行了,那么 r1 的結果是為 0,

  • 情況4:結果還有可能是 0

    • 這種現象叫做指令重排,是 JIT 編譯器在運行時的一些優化,這個現象需要通過大量測驗才能偶爾遇見!

解決方法:

volatile 修飾的變數,可以禁用指令重排,禁止的是加volatile 關鍵字變數之前的代碼重排序,

1.3.3 volatile關鍵字是如何保證有序性的?

  • 當一個共享變數被 volatile 修飾時,它會保證修改的值會被立即更新到主記憶體中,當有其他執行緒讀取該值時,也不會直接讀取作業記憶體中的值,而是直接去主記憶體中讀取,

  • 而普通的共享變數不能保證可見性的,因為普通共享變數被修改后,寫入了作業記憶體中,什么時候寫入主記憶體其實是不可知的,當其他執行緒去讀取是,此時無論是作業記憶體還是主記憶體,可能還是原來的值,因此無法保證可見性,

volatile關鍵字修飾的變數,在每個寫操作之后,都會加入一條store記憶體屏障命令,此命令強制將此變數的最新值從作業記憶體同步至主記憶體;在每個讀操作之前,都會加入一條load記憶體屏障命令,此命強制從主記憶體中將此變數的最新值加載至當前執行緒的作業記憶體中,

1.3.4 volatile關鍵字是如何保證有序性的?

volatile 可以禁止指令重排,保證程式會嚴格按照代碼的先后順序執行,

加了volatile 修飾的共享變數,通過記憶體屏障解決多執行緒下的有序性問題,原理如下:

  • 在每個 volatile 寫操作的前面插入一個 StoreStore 屏障
  • 在每個 volatile 寫操作的后面插入一個StoreLoad屏障
  • 在每個 volatile 讀操作的后面插入一個LoadLoad屏障
  • 在每個 volatile 讀操作的后面插入一個LoadStore屏障

volatile 在寫操作前后插入了記憶體屏障后生成的指令序列示意圖如下:

volatile 在讀操作后面插入了記憶體屏障后生成的指令序列示意圖如下:

1.4 針對long和double型變數的特殊規則

Java記憶體模型要求lock、unlock、read、load、assign、use、store、write這八種操作都具有原子性,但是對于64位的資料型別(long和double),在模型中特別定義了一條寬松的規定:允許虛擬機將沒有被volatile修飾的64位資料的讀寫操作劃分為兩次32位的操作來進行,即允許虛擬機實作自行選擇是否要保證64位資料型別的load、store、read和write這四個操作的原子性,這就是所謂的“long和double的非原子性協定”(Non-Atomic Treatment of double and long Variables),

如果有多個執行緒共享一個并未宣告為volatile的long或double型別的變數,并且同時對它們進行讀取和修改操作,那么某些執行緒可能會讀取到一個既不是原值,也不是其他執行緒修改值的代表了“半個變數”的數值,不過這種讀取到“半個變數”的情況是非常罕見的,經過實際測驗[1],在目前主流平臺下商用的64位Java虛擬機中并不會出現非原子性訪問行為,但是對于32位的Java虛擬機,譬如比較常用的32位x86平臺下的HotSpot虛擬機,對long型別的資料確實存在非原子性訪問的風險,

1.5 先行發生原則

“先行發生”(Happens-Before)原則,它是判斷資料是否存在競爭,執行緒是 否安全的非常有用的手段

先行發生是Java記憶體模型中定義的兩項操作之間的偏序關系,比如說操作A先行發生于操作B,其實就是說在發生操作B之前,操作A產生的影響能被操作B觀察到,“影響”包括修改了記憶體中共享變數的值、發送了訊息、呼叫了方法等,

先行發生原則示例:

// 以下操作在執行緒A中執行 
i = 1; 
// 以下操作在執行緒B中執行 
j = i; 
// 以下操作在執行緒C中執行 
i = 2;

假設執行緒A中的操作“i=1”先行發生于執行緒B的操作“j=i”,那我們就可以確定在執行緒B的操作執行后,變數j的值一定是等于1,得出這個結論的依據有兩個:一是根據先行發生原則,“i=1”的結果可以被觀察到;二是執行緒C還沒登場,執行緒A操作結束之后沒有其他執行緒會修改變數i的值,現在再來考慮執行緒C,我們依然保持執行緒A和B之間的先行發生關系,而C出現在執行緒A和B的操作之間,但是C與B沒有先行發生關系,那j的值會是多少呢?答案是不確定!1和2都有可能,因為執行緒C對變數i的影響可能會被執行緒B觀察到,也可能不會,這時候執行緒B就存在讀取到過期資料的風險,不具備多執行緒安全性,

下面是Java記憶體模型下一些“天然的”先行發生關系,這些先行發生關系無須任何同步器協助就已經存在,可以在編碼中直接使用,如果兩個操作之間的關系不在此列,并且無法從下列規則推匯出來,則它們就沒有順序性保障,虛擬機可以對它們隨意地進行重排序,

  • 程式次序規則(Program Order Rule):在一個執行緒內,按照控制流順序,書寫在前面的操作先行發生于書寫在后面的操作,注意,這里說的是控制流順序而不是程式代碼順序,因為要考慮分支、回圈等結構,
  • 管程鎖定規則(Monitor Lock Rule):一個unlock操作先行發生于后面對同一個鎖的lock操作,這里必須強調的是“同一個鎖”,而“后面”是指時間上的先后,
  • volatile變數規則(Volatile Variable Rule):對一個volatile變數的寫操作先行發生于后面對這個變數的讀操作,這里的“后面”同樣是指時間上的先后,
  • 執行緒啟動規則(Thread Start Rule):Thread物件的start()方法先行發生于此執行緒的每一個動作
  • 執行緒終止規則(Thread Termination Rule):執行緒中的所有操作都先行發生于對此執行緒的終止檢測,我們可以通過Thread::join()方法是否結束、Thread::isAlive()的回傳值等手段檢測執行緒是否已經終止執行,
  • 執行緒中斷規則(Thread Interruption Rule):對執行緒interrupt()方法的呼叫先行發生于被中斷執行緒的代碼檢測到中斷事件的發生,可以通Thread::interrupted()方法檢測到是否有中斷發生,
  • 物件終結規則(Finalizer Rule):一個物件的初始化完成(建構式執行結束)先行發生于它的finalize()方法的開始
  • 傳遞性(Transitivity):如果操作A先行發生于操作B,操作B先行發生于操作C,那就可以得出操作A先行發生于操作C的結論

2. Java與執行緒

2.1 執行緒的實作

實作執行緒主要有三種方式:使用內核執行緒實作(1:1實作),使用用戶執行緒實作(1:N實作),使用用戶執行緒加輕量級行程混合實作(N:M實作),

這三種方式詳細介紹小伙伴可以自行查閱資料,本文這塊知識介紹不作為重點,

2.2 Java執行緒調度

執行緒調度是指系統為執行緒分配處理器使用權的程序,調度主要方式有兩種,分別是協同式(Cooperative Threads-Scheduling)執行緒調度和搶占式(Preemptive Threads-Scheduling)執行緒調度,

2.3 執行緒狀態轉換

Java語言定義了6種執行緒狀態,在任意一個時間點中,一個執行緒只能有且只有其中的一種狀態,并且可以通過特定的方法在不同狀態之間轉換,這6種狀態分別是:

  • 新建(New):創建后尚未啟動的執行緒處于這種狀態,
  • 運行(Runnable):包括作業系統執行緒狀態中的Running和Ready,也就是處于此狀態的執行緒有可能正在執行,也有可能正在等待著作業系統為它分配執行時間,
  • 無限期等待(Waiting):處于這種狀態的執行緒不會被分配處理器執行時間,它們要等待被其他執行緒顯式喚醒,以下方法會讓執行緒陷入無限期的等待狀態:
    • 沒有設定Timeout引數的Object::wait()方法;
    • 沒有設定Timeout引數的Thread::join()方法;
    • LockSupport::park()方法,
  • 限期等待(Timed Waiting):處于這種狀態的執行緒也不會被分配處理器執行時間,不過無須等待被其他執行緒顯式喚醒,在一定時間之后它們會由系統自動喚醒,以下方法會讓執行緒進入限期等待狀態:
    • Thread::sleep()方法;
    • 設定了Timeout引數的Object::wait()方法;
    • 設定了Timeout引數的Thread::join()方法;
    • LockSupport::parkNanos()方法;
    • LockSupport::parkUntil()方法,
  • 阻塞(Blocked):執行緒被阻塞了,“阻塞狀態”與“等待狀態”的區別是“阻塞狀態”在等待著獲取到一個排它鎖,這個事件將在另外一個執行緒放棄這個鎖的時候發生;而“等待狀態”則是在等待一段時間,或者喚醒動作的發生,在程式等待進入同步區域的時候,執行緒將進入這種狀態,
  • 結束(Terminated):已終止執行緒的執行緒狀態,執行緒已經結束執行,

上述6種狀態在遇到特定事件發生的時候將會互相轉換,它們的轉換關系如下圖所示:

面試題參考

  • JMM 記憶體模型、volatile 關鍵字保證有序性和可見性相關問題總結

結語:

非常建議學習Java的小伙伴,買一本周志明老師的《深入理解Java虛擬機(第3版)》去讀一讀,博客和視頻教程,始終不如看書來得實在呀!

后續會陸續更新,這本書的筆記記的差不多了,排版和格式需要花時間整理,文章都會同步到公眾號上,也歡迎大家通過公眾號加入我的交流qun互相討論jvm這塊的知識內容!

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

標籤:其他

上一篇:調戲微軟文言文AI翻譯:“永不舍汝”、“其母之”是什么鬼???

下一篇:庫函式詳解(二)—— 記憶體函式+模擬實作(部分)

標籤雲
其他(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