JAVA多執行緒 猿類的必修內功

2021-08-29 15:56:13 字數 1646 閱讀 1015

1.最早出現的計算機主要是為了解決複雜的計算問題,而早期的計算機只能夠接受一些特定的指令,當使用者在輸入這個指令的時候,計算機才會去工作,如果不輸入指令,計算機就不會工作,因為計算機本身不會儲存指令,很多情況下,計算機都會處於等待狀態,並沒有真正利用計算機本身的資源。於是進入了批處理作業系統的演變過程。

2.批處理作業系統:使用者把需要執行的多個指令寫在磁帶上,然後讓計算機去讀取這個磁帶執行相應的程式,並把結果輸出在另外乙個磁帶上。

3.雖然批處理這種方式能大大提公升計算機資源的利用率,但是會遇到一些問題,比如,作業系統的乙個指令阻塞了,cpu會等到這個指令執行完畢後,再去執行下乙個指令,這樣的話就會使cpu處於等待狀態,無法提高資源的利用率。為了解決這個問題,就出現了程序和執行緒的概念。

注:為了簡便起見,只放了兩個cpu核心。

如圖中所示:現代計算機硬體架構中一般包含兩個甚至更多個cpu核心,為了消除cpu和記憶體之間的速度差異(從記憶體中載入資料和指令的速度和cpu的執行速度差很多很多),提高指令執行效率,每個cpu核心內建了高速暫存器和**快取(l1,l2,l3),其中暫存器分為指令暫存器和資料暫存器兩部分。

當乙個執行緒在cpu上執行的時候,cpu會先將資料和指令從記憶體中讀取載入(load)到快取中,然後進行計算,計算完成後將資料先寫到快取中,當執行完成後,再將快取中的值更新到主記憶體中。當計算機為多核cpu時,就會出現快取一致性問題。

舉個例子:多執行緒環境中進行i++操作。

public class test ).start();}}

}

看下圖:

我們理想中的多執行緒執行的順序是這樣的;執行緒1先將i載入到快取中,cpu執行計算將i+1為2,並將快取中的i賦值為2,最後將快取中的i更新到主記憶體中,此時主記憶體中i為2,執行緒2此時再將i載入到快取中,cpu執行計算將i+1為3,並將快取中的i賦值為3,最後將快取中的i更新到主記憶體中,此時主記憶體中i為3.符合預期。

而現實中,cpu是並行執行的,如果不做同步處理,很有可能打破上述的理想執行過程,導致。在本例子中,有可能出現cpu1執行完step2,但是還未來得及執行step3,;此時cpu0已經執行step4,將i載入到快取記憶體中了,此時快取記憶體中i的值還是1,是乙個過期的值,那麼當cpu0、cpu1執行完step1-step6之後,主記憶體中的i的值可能為2,而不是3,不符合預期,這就是所謂的執行緒安全問題。

現代計算機中解決多核cpu環境中快取一致性問題的解決方案有兩種;一種是匯流排鎖,另一種是mesi協議(一種快取一致性協議)。

匯流排鎖的運作機制:當快取在使用設定為需要保證快取一致性的共享變數時,鎖定這個變數,保證變數只能被當前cpu使用,其他需要使用該變數的cpu需要等待當前cpu執行完畢才能繼續執行。是早期的多核cpu架構中使用的一種快取一致性機制,由於鎖定匯流排的方式會導致cpu等待,所以效能比較低下。而mesi是一種樂觀鎖的實現:當乙個cpu使用某個設定為需要保證快取一致性的變數時,會給這個變數設定乙個記憶體屏障,當其他cpu讀取這個變數時,由於記憶體屏障的存在,會導致cpu等,直到當前cpu使用完成後,將快取中的值儲存到主記憶體中,然後通過mesi協議通知其他cpu從主記憶體中載入最新值。

Java的多執行緒

一 建立 1 新建乙個類,實現runnable介面,重寫run方法,在run方法內寫要完成的任務,在main函式中,使用該類的物件 run 此時是使用main方法的執行緒,並非新建乙個執行緒。class task implements runnable public class testthread...

java多執行緒

在網上看到很有意思的問題,摘下來好好看下 在面試的時候被問了乙個多執行緒的問題 回來仔細思考了一下,多執行緒是否真的能提高了效率?我對多執行緒的理解就是 比如挖乙個隧道,有2種開工方法 1 只在山的一頭挖,直至挖到山的另一頭,從而打通隧道,這可以看成是單執行緒 2 在山的兩頭挖,同時開工,最後在山的...

Java 多執行緒

1。thread類和runnable介面 2。主線程 用thread的static thread currentthread 方法獲得 3。通過實現runnable介面建立執行緒 實現runnable介面的run方法。新執行緒在run 方法返回時結束。注意用這種方法建立程序時,在實現runnable...