频道栏目
首页 > 资讯 > Java > 正文

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

16-12-05        来源:[db:作者]  
收藏   我要投稿

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

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

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上我们的这些方法应该就不能用了。

相关TAG标签
上一篇:判断文件是否存在
下一篇:牛顿迭代法求方程的根
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站