單例模式
定義:(定義都是抽象的,無需過度在意其意義,設計模式這種東西只可意會不可言傳)確保一個類只有一個實例,而且自行實例化并向整個系統提供這個實例。
單例模式寫法有很多種,因地制宜就行,但關鍵點都是在并發問題上:
1.private構造函數
2.public static靜態公有方法
3.保證線程安全,適應多線程并發訪問:(2個關鍵點)
? ? ? ? * 同步鎖synchronized修飾符:避免同時被多個線程訪問,防止線程不同步,保證并發情況下的原子性,可見性,有序性。
? ? ? ? ? * Volatile修飾符:本質是在告訴jvm當前變量在寄存器中的值是不確定的,需要從內存中讀取(它修飾的變量的讀寫操作都必須在內存中進行),保證了多線程共享變量的可見性,原子性(需條件),有序性(需條件))。
4.保證訪問性能(例如單例每次訪問都進行synchronized判斷影響性能)
5.相關:因為單例涉及并發,涉及synchronized和Volatile修飾符,這2者都關系到CUP線程與內存交互,java內存模擬。不論是synchronized還是Volatile都是對下面3個保證性問題進行的操作,只是在某些情況下適用或者不適用。
? ? ? ? 5.1.Java內存模擬JMM(JMM定義了java虛擬機JVM在內存中的工作方式)
JMM定義了多線程之間共享變量的可見性,一個線程對共享變量的寫入何時對另一個線程可見,以及如何在需要的時候對共享變量進行同步。
? ? ? ? 5.2.并發編程必須的3個保證性問題:原子性問題,可見性問題,有序性問題
? ? ? ? ? *原子性:一個操作,要么全部執行并且執行的過程不會被任何因素打斷,要么就不執行。
理解場景:A向B轉賬500元操作;2個步驟,賬戶A減去500元,賬戶B加上500元:A減500èB加500;過程中如果插入個賬戶B取走500元:A減500èB減500èB加500。原本是2步,變為3步。轉賬結果就會發生錯誤。
只有簡單的讀取,儲存操作才是原子操作如I= 10(變量之間的互相賦值不是原子操作),如果要實現更大范圍操作的原子性,可以通過synchronized,因為鎖能保證一個時刻只有一個線程執行代碼塊,從而不存在原子性問題。
? ? ? ? *可見性:當多個線程訪問同一個變量,一個線程修改了變量的值,他線程能立即看到修改的值。
理解場景:CPU1下線程1執行int I = 0 , I = 10,CPU2下線程2執行 j = I ;假若線程1初始化? I = 0 后將 I = 10放入高速緩存中,但是沒有放入主存中,此時線程2執行,從主存中讀取的仍然是? ? ? I = 0 ,j 就不等于10。線程1已經改變了i的值,但是線程2沒有立即看到線程1修改的值,導致可見性問題。
Synchronized的鎖能保證同一時間只有一個線程執行代碼塊的同時,在釋放鎖之前會將對變量的修改刷新到主存當中,因此避免上面場景中的問題,進而解決可見性問題。
? ? ? ? *有序性:程序執行的順序按照代碼的先后順序執行。
注意場景:指令重排序(InstructionReorder):處理器為了提高程序運行效率,可能會對輸入的代碼進行優化,不保證各個語句的執行先后順序同代碼順序一致,但是保證執行結果一致(單線程一致,多線程233)。
Synchronized的鎖能保證同一時間只有一個線程執行代碼塊,相當于單線程執行,自然保證有序性。
Volatile禁止進行指令重排序。
? ? ? ? ? 5.3.使用volatile必須具備以下2個條件:(2個條件就是保證操作是原子性操作,從而保證使用volatile關鍵字的程序在并發時能夠正確執行。)
*對變量的寫操作不依賴于當前值
*該變量沒有包含在具有其他變量的不變式中
? ? ? ? ? 5.4.synchronized關鍵字是防止多個線程同時執行一段代碼,那么就會很影響程序執行效率,而volatile關鍵字在某些情況下性能要優于synchronized,但是要注意volatile關鍵字是無法替代synchronized關鍵字的,因為volatile關鍵字無法保證操作的原子性。
下周不是線程就是工廠模式。。
對于生活理想,應該像宗教徒對待宗教一樣充滿虔誠與熱情!