手写单例模式
面试时候问道单例模式,单例模式时最简单的模式但是想要用好就得费一番力气
饿汉模式
public staticSingleton{ private static Singleton = new Singleton(); private Singleton(){} public static getSingleton(){ return sinleton; }
这样做得目的简单,但是无法做到延迟创建对象,但是我们很多时候都希望对象尽可能延迟加载,从而减小负担,所以懒汉模式如下
public class Singleton{ private static Singleton singleton = null; private Singleton(){} public static Singleton getSingleton(){ if(singleton ==null){ singleton = new Singleton(); } return singleton; } }
考虑线程安全得写法:
public class Singleton { private static volatile Singleton singleton = null; private Singleton(){} public static Singleton getSingleton(){ synchronized (Singleton.class){ if(singleton == null){ singleton = new Singleton(); } } return singleton; } }
兼顾线程和效率得写法
public class Singleton { private static volatile Singleton singleton = null; private Singleton(){} public static Singleton getSingleton(){ if(singleton == null){ synchronized (Singleton.class){ if(singleton == null){ singleton = new Singleton(); } } } return singleton; } }
两次null 看试多余实际上提高了并发度,在单例模式中new的情况非常少,绝大多数进行并行读操作,因此多一次null,减少了绝大多的加锁的操作,执行效率提高的目的达到了。
其他的坑
volatitle 第一层意思被大家熟知,就是该变量工作内存中的修改会马上写入到主存。工作内存时线程独享。主存线程共享。 volatitle第二层意思禁止指令从排序优化。在jdk1.5之前无法保证线程安全。
静态内部类模式
那么,有没有一种延时加载,并且能保证线程安全的简单写法呢?我们可以把Singleton实例放到一个静态内部类中,这样就避免了静态实例在Singleton类加载的时候就创建对象,并且由于静态内部类只会被加载一次,所以这种写法也是线程安全的
public class Singleton { private static class Holder { private static Singleton singleton = new Singleton(); } private Singleton(){} public static Singleton getSingleton(){ return Holder.singleton; } }
枚举写法
public enum Singleton { INSTANCE; private String name; public String getName(){ return name; } public void setName(String name){ this.name = name; } }
使用枚举除了线程安全和防止反射强行调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。因此,Effective Java推荐尽可能地使用枚举来实现单例。
总结
最后,不管采取何种方案,请时刻牢记单例的三大要点:
- 线程安全
- 延迟加载
- 序列化与反序列化安全