餓漢式
餓漢式的代碼很簡單也不是我們的重點。
package singleton_k;/* * @auther 頂風少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 20:41 * @notify * @version 1.0 */ public class Computer { private Computer() { } private static Computer computer = new Computer(); public static Computer getInstance() { return computer; } }
懶漢式-同步鎖
這種synchronized關鍵字的做法是很最簡單的,但是性能較差,對象的創建實際只有一次,剩下的其實都是獲取,但是卻要在獲取時也要同步。
package singleton_k;/* * @auther 頂風少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 20:41 * @notify * @version 1.0 */ public class Computer2 { private Computer2() { } private static Computer2 computer = null; public static synchronized Computer2 getInstance() { if (computer == null) { computer = new Computer2(); } return computer; } }
懶漢式-雙檢查鎖
將synchronized轉移到方法中,假如有10條線程進入方法內,如果判斷==null,也只能有一個線程進入同步代碼塊,進入代碼塊后再次判斷是否==null第一個線程會將對象實例化。而后來的9條線程判斷!=null就不會再次創建。第二次再來10條線程,則第一個if判斷都不會通過,所以不會進入synchronized代碼塊也不會造成線程串行。大大的解決的性能問題。
package singleton_k;/* * @auther 頂風少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 20:41 * @notify * @version 1.0 */ public class Computer3 { private String name = "張三"; private Computer3() { } private static Computer3 computer = null; public static Computer3 getInstance() { //第二個線程判斷對象不為null if (computer == null) { synchronized (Computer3.class) { if (computer == null) { computer = new Computer3(); } } } return computer; } }
但是這里還會有一點問題,如果在第一個線程new之后,但是沒有初始化成員變量。第二個線程在第一次判斷這時已經不是null了,它走到打印處,可能會沒有值。所以使用volatile關鍵字。這個關鍵字的作用禁止CPU使用緩存,禁止指令重排序。
package singleton_k;/* * @auther 頂風少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 20:41 * @notify * @version 1.0 */ public class Computer3 { private String name = "張三"; private Computer3() { } /* volatile 作用1 禁止被他修飾變量發生指令重排序 作用2 禁止CPU使用緩存 * */ private static volatile Computer3 computer = null; public static Computer3 getInstance() { //第二個線程判斷對象不為null if (computer == null) { synchronized (Computer3.class) { if (computer == null) { /*1 new:開辟內存空間 * 2 成員變量初始化 * 3 將內存中的地址賦值給變量 * 此處2和3可能指令重排序 * */ //第一個線程執行完下邊這一句new出來了。 computer = new Computer3(); } } } //此處可能會出錯,出錯了。線程1的指令重排序是132但是2還沒執行就拿不到這個變量的值了 System.out.println(computer.name); return computer; } }
懶漢式-靜態內部類
通過靜態內部類的方式實現單例模式是線程安全的,同時靜態內部類不會在Singleton類加載時就加載,而是在調用getInstance()方法時才進行加載,達到了懶加載的效果。
package singleton_k;/* * @auther 頂風少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 20:41 * @notify * @version 1.0 */ public class Computer4 { private Computer4() { } /* * 通過內部類維護單例JVM在類加載的時候,是互斥的,所以可以保證線程安全問題 * */ private static class Computer4Factory{ private static Computer4 computer4 = new Computer4(); } public static Computer4 getComputer(){ return Computer4Factory.computer4; } }
懶漢式-枚舉
利用枚舉的特性,讓JVM來幫我們保證線程安全和單一實例的問題。除此之外,寫法還特別簡單。
package singleton_k;/* * @auther 頂風少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 20:41 * @notify * @version 1.0 */ public enum Computer4 { INSTANCE; public void show(){ System.out.println("....."); } }
