java设计模式之单例模式
一.应用场景
在实际开发中,当我们要求一个类只能被实例化一次时,比如各种各样的Manager、各种各样的Factory,可以使用单例模式
二.构造单例的常规步骤
- 提供一个private static final 的当前类的实例变量INSTANCE
- 将构造方法设置为private,保证其对外不能通过new关键字实例化
- 通过getInstance的静态方法返回一个唯一的当前类的实例
三.单例的七种写法
-
饿汉模式
public class SingleInstance01 { private static final SingleInstance01 INSTANCE = new SingleInstance01(); private SingleInstance01() {} public static SingleInstance01 getInstance() { return INSTANCE; } }
public class Main { public static void main(String[] args) { SingleInstance01 s1 = SingleInstance01.getInstance(); SingleInstance01 s2 = SingleInstance01.getInstance(); System.out.println(s1 == s2);//输出true } }
类加载到内存后,就会创建一个实例对象
由JVM保证线程安全
JVM保证每一个class只会被load到内存一次,static的变量是在class被load到内存之后,会马上进行实例化,所以也保证了INSTANCE只会被初始化一次
缺点是:不管用到与否,类加载时就会完成实例化 -
懒汉模式
public class SingleInstance02 { private static SingleInstance02 INSTANCE; private SingleInstance02() {} public static SingleInstance02 getInstance() { if(INSTANCE == null) { doSomeThings(); INSTANCE = new SingleInstance02(); } return INSTANCE; } private static void doSomeThings() { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class Main { public static void main(String[] args) { for(int i=0; i<10; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println(SingleInstance02.getInstance().toString()); } }).start(); } } } /*************************** 输出结果 ******************************* com.gale.designpattern.singleinstance.SingleInstance02@3a2cad68 com.gale.designpattern.singleinstance.SingleInstance02@48ac2be5 com.gale.designpattern.singleinstance.SingleInstance02@48ac2be5 com.gale.designpattern.singleinstance.SingleInstance02@4e35792a com.gale.designpattern.singleinstance.SingleInstance02@2cb30bd3 com.gale.designpattern.singleinstance.SingleInstance02@11d32fc4 com.gale.designpattern.singleinstance.SingleInstance02@d084795 com.gale.designpattern.singleinstance.SingleInstance02@48ac2be5 com.gale.designpattern.singleinstance.SingleInstance02@48ac2be5 com.gale.designpattern.singleinstance.SingleInstance02@48ac2be5 ******************************************************************/
虽然达到了按需初始化的目的,却带来了线程不安全的问题
-
在懒汉模式的基础上,通过synchronized锁定类的class对象解决线程不安全的问题,也叫做线程安全的懒汉模式,但也带来了效率上的下降
public class SingleInstance03 { private static SingleInstance03 INSTANCE; private SingleInstance03() {} public static synchronized SingleInstance03 getInstance() { if(INSTANCE == null) { doSomeThings(); INSTANCE = new SingleInstance03(); } return INSTANCE; } private static void doSomeThings() { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class Main { public static void main(String[] args) { for(int i=0; i<10; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println(SingleInstance03.getInstance().toString()); } }).start(); } } } /*************************** 输出结果 ******************************* com.gale.designpattern.singleinstance.SingleInstance03@1469c295 com.gale.designpattern.singleinstance.SingleInstance03@1469c295 com.gale.designpattern.singleinstance.SingleInstance03@1469c295 com.gale.designpattern.singleinstance.SingleInstance03@1469c295 com.gale.designpattern.singleinstance.SingleInstance03@1469c295 com.gale.designpattern.singleinstance.SingleInstance03@1469c295 com.gale.designpattern.singleinstance.SingleInstance03@1469c295 com.gale.designpattern.singleinstance.SingleInstance03@1469c295 com.gale.designpattern.singleinstance.SingleInstance03@1469c295 com.gale.designpattern.singleinstance.SingleInstance03@1469c295 ******************************************************************/
-
试图通过减小同步代码块的方式来提高效率,不可行
public class SingleInstance04 { private static SingleInstance04 INSTANCE; private SingleInstance04() {} public static SingleInstance04 getInstance() { if(INSTANCE == null) { synchronized (SingleInstance04.class) { doSomeThings(); INSTANCE = new SingleInstance04(); } } return INSTANCE; } private static void doSomeThings() { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class Main { public static void main(String[] args) { for(int i=0; i<10; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println(SingleInstance04.getInstance().toString()); } }).start(); } } } /*************************** 输出结果 ******************************* com.gale.designpattern.singleinstance.SingleInstance04@1ca5fe10 com.gale.designpattern.singleinstance.SingleInstance04@1ca5fe10 com.gale.designpattern.singleinstance.SingleInstance04@1ca5fe10 com.gale.designpattern.singleinstance.SingleInstance04@1ca5fe10 com.gale.designpattern.singleinstance.SingleInstance04@21b39199 com.gale.designpattern.singleinstance.SingleInstance04@6fd8b67a com.gale.designpattern.singleinstance.SingleInstance04@b6f008f com.gale.designpattern.singleinstance.SingleInstance04@18f3035c com.gale.designpattern.singleinstance.SingleInstance04@8fddaf1 com.gale.designpattern.singleinstance.SingleInstance04@1dce2a4 ******************************************************************/
-
双重检查的synchronized单例写法
public class SingleInstance05 { private static SingleInstance05 INSTANCE; private SingleInstance05() {} public static SingleInstance05 getInstance() { if(INSTANCE == null) { synchronized (SingleInstance05.class) { if(INSTANCE == null) { doSomeThings(); INSTANCE = new SingleInstance05(); } } } return INSTANCE; } private static void doSomeThings() { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class Main { public static void main(String[] args) { for(int i=0; i<10; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println(SingleInstance05.getInstance().toString()); } }).start(); } } } /*************************** 输出结果 ******************************* com.gale.designpattern.singleinstance.SingleInstance05@7dcec037 com.gale.designpattern.singleinstance.SingleInstance05@7dcec037 com.gale.designpattern.singleinstance.SingleInstance05@7dcec037 com.gale.designpattern.singleinstance.SingleInstance05@7dcec037 com.gale.designpattern.singleinstance.SingleInstance05@7dcec037 com.gale.designpattern.singleinstance.SingleInstance05@7dcec037 com.gale.designpattern.singleinstance.SingleInstance05@7dcec037 com.gale.designpattern.singleinstance.SingleInstance05@7dcec037 com.gale.designpattern.singleinstance.SingleInstance05@7dcec037 com.gale.designpattern.singleinstance.SingleInstance05@7dcec037 ******************************************************************/
-
静态内部类的单例写法(最完美的写法之一)
public class SingleInstance06 { private SingleInstance06() { } private static class SingleInstance06Holder { private static final SingleInstance06 INSTANCE = new SingleInstance06(); } public static SingleInstance06 getInstance() { return SingleInstance06Holder. INSTANCE; } }
public class Main { public static void main(String[] args) { for(int i=0; i<10; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println(SingleInstance06.getInstance().toString()); } }).start(); } } } /*************************** 输出结果 ******************************* com.gale.designpattern.singleinstance.SingleInstance06@418b0eca com.gale.designpattern.singleinstance.SingleInstance06@418b0eca com.gale.designpattern.singleinstance.SingleInstance06@418b0eca com.gale.designpattern.singleinstance.SingleInstance06@418b0eca com.gale.designpattern.singleinstance.SingleInstance06@418b0eca com.gale.designpattern.singleinstance.SingleInstance06@418b0eca com.gale.designpattern.singleinstance.SingleInstance06@418b0eca com.gale.designpattern.singleinstance.SingleInstance06@418b0eca com.gale.designpattern.singleinstance.SingleInstance06@418b0eca com.gale.designpattern.singleinstance.SingleInstance06@418b0eca ******************************************************************/
由JVM保证单例和线程安全
加载外部类时不会加载内部类,而是在getInstance方法被调用时,内部类才会被加载,这样就实现了懒加载 -
枚举单例:不仅可以解决线程同步,还可以防止反序列化(因为枚举类没有构造方法,即使反序列化后,也不能被实例化)(最完美的写法之一)
public enum SingleInstance07 { INSTANCE; public static SingleInstance07 getInstance() { return INSTANCE; } }
public class Main { public static void main(String[] args) { for(int i=0; i<10; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println(SingleInstance07.getInstance().hashCode()); } }).start(); } } } /*************************** 输出结果 ******************************* 1467106563 1467106563 1467106563 1467106563 1467106563 1467106563 1467106563 1467106563 1467106563 1467106563 ******************************************************************/