单例模式的六种实现方式
1、单例模式之饿汉式[可用]
//饿汉式实现单例模式
public class Hungry {
private Hungry(){}
private static Hungry hungry = new Hungry();
public static Hungry getHungry(){
return hungry;
}
}
优点:实现简单,类加载的时候就完成了实例化,避免了线程的同步问题
缺点:无法实现延迟加载,可能会造成内存的浪费(浪费可忽略)
2、单例模式之懒汉式[不可用]
//懒汉式实现单例模式
public class LazySingleton01 {
private LazySingleton01(){}
private static LazySingleton01 lazy ;
public static LazySingleton01 getLazy(){
if(lazy==null){
return new LazySingleton01();
}
return lazy;
}
}
缺点:线程不安全,不推荐使用
存在线程安全问题的过程:在运行过程中可能会存在这么一种情况,有多个线程去调用getLazy方法来获取LazySingleton01实例,就有可能发生,第一个线程在执行if(lazy = =null)的语句时,还没执行new LazySingleton01()时,第二个线程获得的lazy也为null,通过了(lazy==null)的判断,最终两个线程都会new实例。
3、单例模式之懒汉式+同步方法[不推荐使用]
//懒汉式+同步方法实现单例模式
public class LazySingleton02 {
private LazySingleton02(){}
private static LazySingleton02 lazy;
public static synchronized LazySingleton02 getLazy(){
if(lazy==null){
return new LazySingleton02();
}
return lazy;
}
}
线程安全,效率低不推荐使用
对于上述缺陷的改进可能有的人会想到如下的代码:
public class LazySingleton03 {
private LazySingleton03(){}
private static LazySingleton03 lazy;
public static LazySingleton03 getLazy(){
if(lazy==null){
synchronized(LazySingleton03.class){
return new LazySingleton03();
}
}
return lazy;
}
}
该写法一样是线程不安全的,当一个线程还没有实例化LazySingleton03时,另一个线程执行到if(lazy==null)这个判断语句时就会进入if语句,虽然加了锁,但是等到第一个线程执行完lazy=new LazySingleton03()释放出这个锁时,另一个进入if语句的线程同样会实例化另外一个LazySingleton03对象。
经过一步步的探索,有了双重校验锁的懒汉式写法:
4、单例模式之懒汉式+双重校验锁[推荐使用]
//双重校验锁+懒汉式
public class DoubleCheckSingleton {
private DoubleCheckSingleton(){}
private volatile static DoubleCheckSingleton instance;
public static DoubleCheckSingleton getInstance(){
if(instance==null){
synchronized (DoubleCheckSingleton.class){
if(instance==null){
return new DoubleCheckSingleton();
}
}
}
return instance;
}
}
优点:线程安全,延迟加载,效率较高
注意此处的volatile:
//双重校验锁+懒汉式
public class DoubleCheckSingleton {
private Socket socket;
private DoubleCheckSingleton(){
this.socket=new Socket();
}
private volatile static DoubleCheckSingleton instance;
public static DoubleCheckSingleton getInstance(){
if(instance==null){
synchronized (DoubleCheckSingleton.class){
if(instance==null){
return new DoubleCheckSingleton();
}
}
}
return instance;
}
}
根据JVM运行时指令重排序和Happens-Before的规则,instance和socket之间的实例化的顺序并没有什么关系约束,谁在前谁在后都有可能,如果instance最先实例化,那么socket就不会实例化,调用这个方法就会产生空指针异常------>
解决这个方法很简单,加一个关键字 volatite
这个关键字能够保证实例化的顺序的相对位置不变,instance实例化永远在socket之后
5、单例模式之静态内部类[推荐使用]
//静态内部类实现单例模式
public class StaticInner {
private StaticInner(){}
private static class inner{
private static StaticInner instance=new StaticInner();
}
public static StaticInner getInstance(){
return inner.instance;
}
}
优点:避免了线程不安全,延迟加载,效率高
详解:这个方法并没有事先声明instance的静态成员,而是把它放到了静态内部类inner中,inner中定义了静态变量,并直接进行了实例化,当inner被主动引用的时候就会创建实例,StaticInner在实例创建过程中被收集到()方法中,这个方法是同步方法,
保证了内存的可见性,JVM指令的顺序执行和原子性。该方法也是单例模式的最好设计之一。
6、单例模式之枚举[推荐使用]
//枚举实现单例模式
public enum EnumSingleton {
INSTANCE;
private EnumSingleton(){}
public void method(){}
}
借助JDK1.5中添加的枚举来实现单例模式
优点:不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,实现非常简单而且最安全可谓很完美。