单例模式
单例模式
单利模式有很多种实现方式,每一种方式都有自己合适的场合。常用的单利有:懒汉式、饿汉式、线程安全式、双重检查式和登记式。
实现单利最重要的一点是构造方法是private的,这使得实例不能被外界所创建。
懒汉式
这是最简单的一种单利模式,但是带来的问题是线程不安全。这里所谓的不安全是指在创建第一个实例时是线程不安全的。可以看到,当我们初次使用时才会创建实例,所以叫懒汉式。
1 public class SimpleSingleton { 2 3 private static SimpleSingleton simpleSingleton; 4 5 //必须为private,这是实例独一无二的保证 6 private SimpleSingleton(){ 7 8 } 9 10 public static SimpleSingleton getInstance(){ 11 //如果不存在则实例一个 12 if(simpleSingleton==null){ 13 //创建第一个实例时是线程不安全的 14 simpleSingleton = new SimpleSingleton(); 15 } 16 return simpleSingleton; 17 } 18 }
线程安全式
为了解决线程安全的问题,我们只需要在获得实例的方法上同步即可,但是这就会造成程序性能下降。
1 public class SafeSingleton { 2 3 private static SafeSingleton safeSingleton; 4 5 // 必须为private,这是实例独一无二的保证 6 private SafeSingleton() { 7 8 } 9 10 //保证线程安全 11 public static synchronized SafeSingleton getInstance() { 12 // 如果不存在则实例一个 13 if (safeSingleton == null) { 14 safeSingleton = new SafeSingleton(); 15 } 16 return safeSingleton; 17 } 18 }
饿汉式
可以看到,在JVM加载这个类时,就会创建实例。JVM能保证在任何线程访问到eagerSingleton实例之前将其创建完成。
1 public class EagerSingleton { 2 3 //JVM加载这个类时就创建 4 private static EagerSingleton eagerSingleton = new EagerSingleton(); 5 6 private EagerSingleton(){}; 7 8 public static EagerSingleton getInstance(){ 9 return eagerSingleton; 10 } 11 }
双重检查
线程安全式中,同步了整个getInstance方法才保证了线程安全,会浪费很多性能。于是我们可以使用双重检查式,它将只在初次创建实例的时候实现同步。(了解锁优化的同学可以理解为减少锁的颗粒度)
1 public class DoubleCheckSingleton { 2 3 private volatile static DoubleCheckSingleton doubleCheckSingleton; 4 5 private DoubleCheckSingleton() { 6 }; 7 8 public static DoubleCheckSingleton getInstance() { 9 //这里没有加synchronized 10 if (doubleCheckSingleton == null) { 11 //很多线程都能到达这里 12 synchronized (DoubleCheckSingleton.class) { 13 //有可能已经有线程更早一步创建了单利 14 if (doubleCheckSingleton == null) 15 doubleCheckSingleton = new DoubleCheckSingleton(); 16 } 17 } 18 return doubleCheckSingleton; 19 } 20 }
登记式
虽然和饿汉式一样使用了静态域,但是此种方式的实例加载是Lazy的。
1 public class StaticSingleton { 2 3 // 利用了 classloder 机制来保证初始化 instance 时只有一个线程 4 private static class SingletonHolder { 5 private static final StaticSingleton INSTANCE = new StaticSingleton(); 6 } 7 8 private StaticSingleton() { 9 } 10 11 public static final StaticSingleton getInstance() { 12 return SingletonHolder.INSTANCE; 13 } 14 }
不忘初心