Java设计模式之单例模式(Singleton)

在开发中,我们要求一个类的实例始终只有一个。例如,存缓池、线程池、数据库连接池……

下面先从一个简单的单例开始介绍


public class SimpleSingleton {
    private static SimpleSingleton s= new SimpleSingleton();

    //set constructor inaccessible
    private SimpleSingleton(){}

    public static SimpleSingleton getInstance(){
        return s;
    }
}

这样我们每次调用SimpleSingleton.getInstance();
就可以获得始终唯一的SimpleSingleton对象。
这个是线程安全的。但是,我们不希望他加载类的时候创建实例。(static在类加载的时候进行执行)

====================================================
于是,我们就要对SimpleSingleton的实例化进行延迟,让他在第一次调用getInstance()时才进行创建。


public class Singleton2 {
    private static Singleton2 s=null;
    private Singleton2(){}

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

注意这种方式不是线程安全的。(当线程一执行完判断但还没开始创建对象时,线程二获得执行权,他在判断时s仍是空的。于是,线程一二都会创建对象)

====================================================
那我们要变成线程安全的,就要使用synchronized关键字来保证线程的安全。若我们这么做:

public class Singleton3 {
    private static Singleton3 s=null;
    private Singleton3(){}
    public synchronized static Singleton3 getInstance(){
        if(s==null)
            s=new Singleton3();
        return s;
    }
}

这种方法虽然是线程安全的,但是,执行效率会非常的低。因为这个方法始终只会让一个线程访问。

====================================================
于是,我们的重头戏来了Double-Check locking


public class Singleton4 {
    private volatile static Singleton4 s=null;
    private Singleton4(){}

    public static Singleton4 getInstance(){
        if(s==null){
            synchronized (Singleton4.class) {
                if(s==null){
                    s=new Singleton4();
                }
            }
        }
        return s;
    }
}

volatile属性具有synchronized可见性,即线程能自动发现volatile的最新属性。
这样我们最终解决了线程安全问题!
但是,要是我们的Singleton类实现了Serializable接口,那么在反序列化的时候还是会打破我们的单例原则。

import java.io.Serializable;

/**
 * 解决Serializable接口带来的反序列化问题
 * @author majin
 */
public class SinSerial implements Serializable{
    private volatile static SinSerial s=null;
    private SinSerial(){}

    public static SinSerial getInstance(){
        if(s==null){
            synchronized (SinSerial.class) {
                if(s==null){
                    s=new SinSerial();
                }
            }
        }
        return s;
    }

    //this method will be called immediately after this class is deserialized
    private Object readResolve(){
        return s;
    }
}

这样线程安全、反序列化的问题我们都解决了。

注意:我们在这里的单例都是说在一个JVM上,如果是分布式的JVM上我们的这些方法应该就不能用了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值