单例模式的5种实现

声明:写这个内容主要是便于自己记忆和理解,这边有更好的文章http://www.tekbroaden.com/singleton-java.html

第一种:饿汉模式--即类加载时就进行实例化,无论是否会用到该实例,都要预先加载,而我们一般希望延迟加载,减小负载。

 

public class Singleton {
    //恶汉模式,即类加载时就创建实例
    //1.私有化构造器
    private Singleton(){
    }

    //2.声明并实例化,注意是私有,并且为静态的类成员
    private static Singleton instance = new Singleton();

    //3.使用公有的静态函数返回实例
    public static Singleton getInstance(){
        return instance;
    }
}

第二种:懒汉模式--一开始只声明,不进行实例化。当需要使用实例时才创建。

public class Singleton2 {
    //1.私有化构造器
    private Singleton2(){}
    //2.声明,注意是私有,并且为静态的类成员
    private static Singleton2 instance;
    //3.使用公有的静态函数返回实例
    public static Singleton2 getInstance(){
        if(instance == null){
            instance = new Singleton2();
        }
        return instance;
    }
}

这种写法线程不安全,当有多个线程访问时,可能会创建多个实例,因此可以加一个锁。

public static synchronized Singleton2 getInstance(){
        if(instance == null){
            instance = new Singleton2();
        }
        return instance;
    }

但是这个方法每次都要进行同步,即使实例存在的时候,所以效率不高,由此引出下一种方法。

 

第三种:双重检查锁,首先加了双重检查,如果实例存在的话,就不会进入同步的代码了,提高了效率。第二个要注意的地方是实例声明的时候加了一个volatile关键字,这个关键字有两层语义,1是可见性,2是禁止指令重排序优化。因为JVM会自己进行指令排序的优化,当多线程访问的时候,指令的执行顺序与源代码不一样,就会出问题。volatile就是解决这个问题的,但这要在jdk1.5之后才有作用。

public class Singleton2 {
    //1.私有化构造器
    private Singleton2(){}
    //2.声明,注意是私有,并且为静态的类成员
    private static volatile Singleton2 instance;
    //3.使用公有的静态函数返回实例
    public static  Singleton2 getInstance(){
        if(instance == null){
            synchronized (Singleton2.class){
                if(instance == null) instance = new Singleton2();
            }
        }
        return instance;
    }
}

 

第四种:静态内部类,因为是静态内部类,所以Sington3类加载时不会创建实例,而且因为是静态内部类所有只会加载一次,所以也是线程安全的。

public class Singleton3 {
    //1.私有化构造器
    private Singleton3(){
    }

    //静态的内部类来创建实例,这样只有调用实例方法的时候才会创建
    private static class Holder{
        private static Singleton3 instance = new Singleton3();
    }

    //3.使用公有的静态函数返回实例
    public static Singleton3 getInstance(){
        return Holder.instance;
    }
}


上述这四种方法都有缺点:序列化和反序列化需要额外的工作来实现,要注意反射强行调用私有构造方法的问题。

 

这篇文章中的说明http://www.tekbroaden.com/singleton-java.html

都需要额外的工作(Serializable、transient、readResolve())来实现序列化,否则每次反序列化一个序列化的对象实例时都会创建一个新的实例。
可能会有人使用反射强行调用我们的私有构造器(如果要避免这种情况,可以修改构造器,让它在创建第二个实例的时候抛异常)。

第五种:枚举类

public enum Singleton4 {
    INSTANCE;

    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

 

还有一种接口形式实现的枚举类,感觉很厉害!!!

public interface MySingleton {
    void doSomething();
}
public enum Singleton5 implements MySingleton { 
    INSTANCE { 
        @Override 
        public void doSomething(){
        }
     }; 
    public static MySingleton getInstance(){
        return INSTANCE; 
    } 
}

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值