當系統中某個類對象只須要實例化一次的時候,咱們就用單例來實現。因此單例模式就是用來建立獨一無二,只能有一個實例的對象的一直實現方式。數據庫
好比線程池,緩存,鏈接數據庫的Connection等等。緩存
1,餓漢式安全
package singleton; /** * @ClassName SingletonDemo1 * @Description 餓漢式 * 類加載到內存後,就實例化一個單例對象,JVM保證線程安全 * 惟一缺點:無論用不用都會進行加載,可是影響不大,其實是最適用的一種方式 * @Author liuyi * @Date 2020/6/7 12:22 * @Version 1.0 */ public class SingletonDemo1 { //兩種方式初始化實例,兩種方式的效果是同樣的 //靜態常量方式 // private static final SingletonDemo1 instance = new SingletonDemo1(); //靜態塊方式 private static final SingletonDemo1 instance; static { instance = new SingletonDemo1(); } private SingletonDemo1(){ } public static SingletonDemo1 getInstance(){ return instance; } public static void main(String[] args) { SingletonDemo1 instance1 = SingletonDemo1.getInstance(); SingletonDemo1 instance2 = SingletonDemo1.getInstance(); System.out.println(instance1==instance2); //返回的結果爲true,說明無論取多少次都是同一個實例 } }
2,懶漢式多線程
package singleton; /** * @ClassName SingletonDemo2 * @Description 懶漢式 * 須要使用該對象的時候再去實例化 * 缺點:會產生線程安全問題 * @Author liuyi * @Date 2020/6/7 13:23 * @Version 1.0 */ public class SingletonDemo2 { private static SingletonDemo2 instance; private SingletonDemo2(){ } public static SingletonDemo2 getInstance(){ if(instance==null){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } instance = new SingletonDemo2(); } return instance; } public static void main(String[] args) { //爲何說線程不安全 //由於使用了全局的靜態變量,會形成多線程訪問的時候會產生不惟一的實例 for (int i = 0; i <100 ; i++) { new Thread(()->{ System.out.println(getInstance()); }).start(); } //打印的對象徹底亂了,根本不是同一個實例 } }
3,餓漢式(加鎖)ide
package singleton; /** * @ClassName SingletonDemo2 * @Description 懶漢式(加鎖) * 缺點:效率低 * @Author liuyi * @Date 2020/6/7 13:23 * @Version 1.0 */ public class SingletonDemo3 { private static SingletonDemo3 instance; private SingletonDemo3(){ } public synchronized static SingletonDemo3 getInstance(){ if(instance==null){ instance = new SingletonDemo3(); } return instance; } public static void main(String[] args) { //加了鎖解決了懶漢式的線程不安全問題,可是這樣效率就會明顯下降 for (int i = 0; i <100 ; i++) { new Thread(()->{ System.out.println(getInstance()); }).start(); } } }
4,雙重檢查測試
package singleton; /** * @ClassName SingletonDemo2 * @Description 雙重檢查(比較完美的寫法) * @Author liuyi * @Date 2020/6/7 13:23 * @Version 1.0 */ public class SingletonDemo4 { //必須加volatile關鍵字,防止指令重排 private static volatile SingletonDemo4 instance; private SingletonDemo4(){ } public synchronized static SingletonDemo4 getInstance(){ //爲何要進行雙重檢查 //好比兩個線程同時進入該方法,都拿到instance爲空,其中一個拿到鎖並new了一個實例, //此時另一個線程它並不知道你已經new了實例,因此當它拿到鎖以後會繼續new一個實例 //因此若是在鎖裏面繼續判斷一次是頗有必需要的 if(instance==null){ synchronized (SingletonDemo4.class){ if(instance==null){ instance = new SingletonDemo4(); } } } return instance; } public static void main(String[] args) { for (int i = 0; i <100 ; i++) { new Thread(()->{ System.out.println(getInstance()); }).start(); } } }
5,靜態內部類方式spa
package singleton; /** * @ClassName SingletonDemo5 * @Description 靜態內部類方式 * JVM保證線程安全 * 加載外部類是不會加載內部類,實現了懶加載 * 最完美的寫法 * @Author liuyi * @Date 2020/6/7 13:52 * @Version 1.0 */ public class SingletonDemo5 { private SingletonDemo5(){ } private static class SingletonDemo5Inside{ private static final SingletonDemo5 instance = new SingletonDemo5(); } public static SingletonDemo5 getInstance(){ return SingletonDemo5Inside.instance; } public static void main(String[] args) { for (int i = 0; i <100 ; i++) { new Thread(()->{ System.out.println(SingletonDemo5.getInstance()); }).start(); } } }
6,枚舉方式線程
package singleton; /** * @Author liuyi * @Description 枚舉單例 * 不只能夠解決線程同步,還能夠防止反序列化(由於枚舉類沒有構造方法) * @Date 17:28 2020/6/7 * @Param * @return **/ public enum SingletonDemo6 { instance; public void test(){ System.out.println("測試測試"); } public static void main(String[] args) { instance.test(); for (int i = 0; i <100 ; i++) { new Thread(()->{ System.out.println(SingletonDemo6.instance); }).start(); } } }
這幾種實現單例的方式中,枚舉單例是最完美的,由於枚舉單例能夠防止反序列話,也能夠防止經過反射的方式去建立實例,可是實際運用中最適用的仍是餓漢式。由於既然你使用了單例,爲何還要用反射呢,這樣就屬於搞破壞了。code