學習Java的同學注意了!!!
學習進程中遇到甚么問題或想獲得學習資源的話,歡迎加入Java學習交換群,群號碼:183993990 我們1起學Java!
使用高速緩存來作為內存與處理器之間的緩沖,將運算需要用到的數據復制到緩存中,讓計算能快速進行;當運算結束后再從緩存同步回內存當中,這樣處理器就無需等待緩慢的內存讀寫了。
緩存1致性:多處理器系統中,由于同享同1主內存,當多個處理器的運算任務都設計到同1塊內存區域時,將可能致使各自的緩存數據不1致的情況,則同步回主內存時需要遵守1些協議。
亂序履行優化:為了使得處理器內部的運算單位能盡可能被充分利用。
目標是定義程序中各個變量的訪問規則。(包括實例字段、靜態字段和構成數組的元素,不包括局部變量和方法參數)
內存間交互操作:
Lock(鎖定):作用于主內存中的變量,把1個變量標識為1條線程獨占的狀態。
Read(讀取):作用于主內存中的變量,把1個變量的值從主內存傳輸到線程的工作內存中。
Load(加載):作用于工作內存中的變量,把read操作從主內存中得到的變量的值放入工作內存的變量副本中。
Use(使用):作用于工作內存中的變量,把工作內存中1個變量的值傳遞給履行引擎。
Assign(賦值):作用于工作內存中的變量,把1個從履行引擎接收到的值賦值給工作內存中的變量。
Store(存儲):作用于工作內存中的變量,把工作內存中的1個變量的值傳送到主內存中。
Write(寫入):作用于主內存中的變量,把store操作從工作內存中得到的變量的值放入主內存的變量中。
Unlock(解鎖):作用于主內存中的變量,把1個處于鎖定狀態的變量釋放出來,以后可被其它線程鎖定。
規則:
運算結果其實不依賴變量確當前值、或確保只有單1的線程修改變量的值。
變量不需要與其他的狀態變量共同參與不變束縛。
原子性:基本數據類型的訪問讀寫是具有原子性的,synchronized塊之間的操作也具有原子性。
可見性:指當1個線程修改了同享變量的值,其他線程能夠立即得知這個修改。synchronized(規則8)和final可以保證可見性。Final修飾的字段在構造器中1旦被初始化完成,并且構造器沒有把this的援用傳遞出去,那末在其他線程中就可以看見final字段的值。
有序性:volatile本身包括了制止指令重排序的語義,而synchronized則是由規則5取得的,這個規則決定了持有同1個所的兩個同步塊只能串行地進入。
Java內存模型中定義的兩項操作之間的偏序關系,如果操作A先行產生于操作B,其實就是說在產生操作B之前,操作A產生的影響能被操作B視察到。
程序次序規則:在1個線程內,依照代碼控制流順序,在前面的操作先行產生于后面的操作。
管程鎖定規則:1個unlock操作先行產生于后面對同1個鎖的lock操作。
Volatile變量規則:對1個volatile變量的寫操作先行產生于后面對這個變量的讀操作。
線程啟動規則:Thread對象的start()方法先行產生于此線程的每一個操作。
線程終止規則:線程中的所有操作都先行產生于對此線程的終止檢測。
線程中斷規則:對線程的interrupt()方法的調用先行產生于被中斷線程的代碼檢測中斷事件的產生。
對象終結過則:1個對象的初始化完成先行產生于它的finalize()方法的開始。
傳遞性:如果操作A先行產生于操作B,操作B現象產生于操作C,那末就能夠得出操作A先行產生于操作C的結論。
時間上的前后順序與先行產生原則之間基本上沒有太大的關系。
使用內核線程實現:
內核線程Kernel Thread:直接由操作系統內核支持的線程,這類線程由內核類完成線程切換,內核通過操縱調度器對線程進行調度,并負責將線程的任務映照到各個處理器上。
輕量級進程Light Weight Process:每一個輕量級進程都由1個內核線程支持。
局限性:各種進程操作都需要進行系統調用(系統調用代價相對較高,需要在用戶態和內核態中來回切換);輕量級進程要消耗1定的內核資源,1次1個系統支持輕量級進程的數量是有限的。
使用用戶線程實現:
用戶線程:完全建立在用戶空間的線程庫上,系統內核不能直接感知到線程存在的實現。用戶線程的建立、同步、燒毀和調度完全在用戶態中完成,不需要內核的幫助。所有的線程操作都需要用戶程序自己處理。
混合實現:
將內核線程和用戶線程1起使用的方式。操作系統提供支持的輕量級進程則作為用戶線程和內核線程之間的橋梁。
Sun JDK,它的Windows版和Linux版都是使用1對1的線程模型來實現的,1條Java線程映照到1條輕量級進程當中。
線程調度是指系統為線程分配處理器使用權的進程:協同式、搶占式。
協同式:線程的履行時間由線程本身控制,線程把自己的工作履行完了以后,要主動通知系統切換到另外一個線程上。壞處:線程履行時間不可控制。
搶占式:每一個線程將由系統來分配履行時間,線程的切換不由線程本身來決定。Java使用該種調用方式。
線程優先級:在1些平臺上(操作系統線程優先級比Java線程優先級少)不同的優先級實際會變得相同;優先級可能會被系統自行改變。
線程狀態:
新建NEW:
運行RUNNABLE:
無窮期等待WAITING:等得其他線程顯式地喚醒。
沒有設置Timeout參數的Object.wait();沒有設置Timeout參數的Thread.wait()。
限期等待TIMED_WAITING:在1定時間以后會由系統自動喚醒。
設置Timeout參數的Object.wait();設置Timeout參數的Thread.wait();Thread.sleep()方法。
阻塞BLOCKED:等待獲得1個排它鎖,等待進入1個同步區域。
結束TERMINATED:
線程安全:當多個線程訪問1個對象時,如果不用斟酌這些線程在運行時環境下的調度和交換履行,也不需要進行額外的同步,或調用方進行任何其他的調和操作,調用這個對象的行動都可以取得正確的結果,那這個對象就是線程安全的。
不可變:只要1個不可變的對象被正確地構建出來。使用final關鍵字修飾的基本數據類型;如果同享數據是1個對象,那就需要保證對象的行動不會對其狀態產生任何影響(String類的對象)。方法:把對象中帶有狀態的變量都申明為final,如Integer類。有:枚舉類型、Number的部份子類(AtomicInteger和AtomicLong除外)。
絕對線程安全:
相對線程安全:對這個對象單獨的操作是線程安全的。1般意義上的線程安全。
線程兼容:需要通過調用端正確地使用同步手段來保證對象在并發環境中安全地使用。
線程對峙:不管調用端是不是采取了同步措施,都沒法在多線程環境中并發使用的代碼。有:System.setIn()、System.setOut()、System.runFinalizersOnExit()
Synchronized關鍵字:編譯后會在同步塊前后分別構成monitorenter和monitorexit這兩個字節碼指令。這兩個指令都需要1個援用類型的參數來指明要鎖定和解鎖的對象。如果沒有明確指定對象參數,那就根據synchronized修飾的是實例方法還是類方法,去取對應的對象實例或Class對象來作為鎖對象。
在履行monitorenter指令時,首先嘗試獲得對象的鎖,如果沒有被鎖定或當前線程已具有了該對象的鎖,則將鎖計數器加1,相應的履行moniterexit時,將鎖計數器減1,當計數器為0時,鎖就被釋放了。如果獲得對象鎖失敗,則當前線程就要阻塞等待。
ReentrantLock相對synchronized的高級功能:
等待可中斷:當持有鎖的線程長時間不釋放鎖時,正在等待的線程可以選擇放棄等待,改成處理其他事情。
公平鎖:多個線程在等待同1個鎖時,必須依照申請鎖的事件順序來1次獲得鎖;而非公平鎖在被釋放時,任何1個等待鎖的線程都有機會取得鎖。Synchronized中的鎖是非公平鎖,ReentrantLock默許也是非公平鎖。
鎖綁定多個條件:1個ReentrantLock對象可以同時綁定多個Condition對象。
基于沖突檢測的樂觀并發策略:先進行操作,如果沒有其他線程爭用同享數據,那操作就成功了;如果同享數據有爭用,產生了沖突,那就再進行其他的補償措施(1般是不斷的嘗試,直到成功為止)。
AtomicInteger等原子類中提供了方法實現了CAS指令。
可重入代碼:可以在代碼履行的任什么時候刻中斷它,轉而去履行另外一段代碼,而在控制權返回后,原來的程序不會出現任何毛病。特點:不依賴存儲在堆上的數據和公用的系統資源、用到的狀態量都由參數傳入,不調用非可重入的方法等。如果1個方法,它的返回結果是可以預測的,只要出入了相同的數據,就可以返回相同的結果,那它就滿足可重入性的要求。
線程本地存儲:如果1段代碼中所需要的數據必須與其它代碼同享,那就看看這些同享數據的代碼是不是能保證在同1個線程中履行。
ThreadLocal:線程級別的局部變量,為每一個使用該變量的線程提供1個獨立的變量副本,每一個線程修改副本時不影響其他線程對象的副本。ThreadLocal實例通常作為靜態私有字段出現在1個類中。
為了讓線程等待,讓線程履行1個忙循環(自旋)。需要物理機器有1個以上的處理器。自旋等待雖然避免了線程切換的開消,帶它是要占用途理器時間的,所以如果鎖被占用的時間很短,自旋等待的效果就會非常好,反之自旋的線程只會白白消耗處理器資源。自旋次數的默許值是10次,可使用參數-XX:PreBlockSpin來更改。
自適應自旋鎖:自旋的時間不再固定,而是由前1次在同1個鎖上的自旋時間及鎖的具有者的狀態來決定。
指虛擬機即時編譯器在運行時,對1些代碼上要求同步,但是被檢測到不可能存在同享數據競爭的鎖進行清除(逃逸分析技術:在堆上的所有數據都不會逃逸出去被其它線程訪問到,可以把它們當做棧上數據對待)。
如果虛擬機探測到有1串零碎的操作都對同1個對象加鎖,將會把加鎖同步的范圍擴大到全部操作序列的外部。
HotSpot虛擬機的對象的內存布局:對象頭(Object Header)分為兩部份信息嗎,第1部份(Mark Word)用于存儲對象本身的運行時數據,另外一個部份用于存儲指向方法區對象數據類型的指針,如果是數組的話,還會由1個額外的部份用于存儲數組的長度。
32位HotSpot虛擬機中對象未被鎖定的狀態下,Mark Word的32個Bits空間中25位用于存儲對象哈希碼,4位存儲對象分代年齡,2位存儲鎖標志位,1位固定為0。
HotSpot虛擬機對象頭Mark Word
存儲內容 |
標志位 |
狀態 |
對象哈希碼、對象分代年齡 |
01 |
未鎖定 |
指向鎖記錄的指針 |
00 |
輕量級鎖定 |
指向重量級鎖的指針 |
10 |
膨脹(重量級鎖) |
空,不記錄信息 |
11 |
GC標記 |
偏向線程ID,偏向時間戳、對象分代年齡 |
01 |
可偏向 |
在代碼進入同步塊時,如果此同步對象沒有被鎖定,虛擬機首先將在當前線程的棧幀中建立1個名為鎖記錄(Lock Record)的空間,用于存儲所對象目前的Mark Word的拷貝。然后虛擬機將使用CAS操作嘗試將對象的Mark Word更新為履行Lock Record的指針。如果成功,那末這個線程就具有了該對象的鎖。如果更新操作失敗,虛擬機首先會檢查對象的Mark Word是不是指向當前線程的棧幀,如果是就說明當前線程已具有了這個對象的鎖,否則說明這個對象已被其它線程搶占。如果有兩條以上的線程爭用同1個鎖,那輕量級鎖就不再有效,要膨脹為重量級鎖。
解鎖進程:如果對象的Mark Word依然指向著線程的鎖記錄,那就用CAS操作把對象當前的Mark Word和和線程中復制的Displaced Mark Word替換回來,如果替換成功,全部進程就完成。如果失敗,說明有其他線程嘗試過獲得該鎖,那就要在釋放鎖的同時,喚醒被掛起的線程。
輕量級鎖的根據:對絕大部份的鎖,在全部同步周期內都是不存在競爭的。
傳統鎖(重量級鎖)使用操作系統互斥量來實現的。
目的是消除在無競爭情況下的同步原語,進1步提高程序的運行性能。鎖會偏向第1個取得它的線程,如果在接下來的履行進程中,該鎖沒有被其它線程獲得,則持有鎖的線程將永久不需要再進行同步。
當鎖第1次被線程獲得的時候,虛擬機將會把對象頭中的標志位設為01,同時使用CAS操作把獲得到這個鎖的線程的ID記錄在對象的Mark Word當中,如果成功,持有偏向鎖的線程以后每次進入這個鎖相干的同步塊時,都可以不進行任何同步操作。
當有另外一個線程去嘗試獲得這個鎖時,偏向模式就宣布結束。根據所對象目前是不是處于被鎖定的狀態,撤消偏向后恢復到未鎖定或輕量級鎖定狀態。
操作系統的兩種運行級別,intel cpu提供-Ring33種運行模式。
Ring0是留給操作系統代碼,裝備驅動程序代碼使用的,它們工作于系統核心態;而Ring3則給普通的用戶程序使用,它們工作在用戶態。運行于處理器核心態的代碼不受任何的限制,可以自由地訪問任何有效地址,進行直接端口訪問。而運行于用戶態的代碼則要遭到處理器的諸多檢查,它們只能訪問映照其地址空間的頁表項中規定的在用戶態下可訪問頁面的虛擬地址,且只能對任務狀態段(TSS)中I/O許可位圖(I/O Permission Bitmap)中規定的可訪問端口進行直接訪問。
在其他線程調用此對象的notify()或notifyAll()方法,或超過指定時間量前,當前線程T等待(線程T必須具有該對象的鎖)。線程T被放置在該對象的休息區中,并釋放鎖。在被喚醒、中斷、超時的情況下,從對象的休息區中刪除線程T,并重新進行線程調度。1旦線程T取得該對象的鎖,該對象上的所有同步申明都被恢復到調用wait()方法時的狀態,然后線程T從wait()方法返回。如果當前線程在等待之前或在等待時被任何線程中斷,則會拋出 InterruptedException。在按上述情勢恢復此對象的鎖定狀態時才會拋出此異常。在拋出此異常時,當前線程的中斷狀態被清除。
只有該對象的鎖被釋放,其實不會釋放當前線程持有的其他同步資源。
喚醒在此對象鎖上等待的單個線程。此方法只能由具有該對象鎖的線程來調用。
在指定的毫秒數內讓當前正在履行的線程休眠(暫停履行),此操作遭到系統計時器和調度程序精度和準確性的影響。監控狀態仍然保持、會自動恢復到可運行狀態,不會釋放對象鎖。如果任何線程中斷了當前線程。當拋出InterruptedException異常時,當前線程的中斷狀態被清除。讓出CPU分配的履行時間。
thread.join():在1個線程對象上調用,使當前線程等待這個線程對象對應的線程結束。
Thread.yield():暫停當前正在履行的線程對象,并履行其他線程。
thread.interrupt()
中斷線程,停止其正在進行的1切。中斷1個不處于活動狀態的線程不會有任何作用。
如果線程在調用Object類的wait()方法、或join()、sleep()方法進程中受阻,則其中斷狀態將被清除,并收到1個InterruptedException。
Thread.interrupted():檢測當前線程是不是已中斷,并且清除線程的中斷狀態(回到非中斷狀態)。
thread.isAlive():如果線程已啟動且還沒有終止,則為活動狀態。
thread.setDaemon():需要在start()方法調用之前調用。當正在運行的線程都是后臺線程時,Java虛擬機將退出。否則當主線程退出時,其他線程依然會繼續履行。
如果1個靜態方法被申明為synchronized,則同等于在這個方法上調用synchronized(類.class)。當1個線程進入同步靜態方法中時,其他線程不能進入這個類的任何靜態同步方法。
下一篇 顏色選擇器