Java设计模式4:单例模式

Java设计模式4:单例模式
单例模式
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。单例模式有以下特点:
1、单例类只能有一个实例
2、单例类必须自己创建自己的唯一实例
3、单例类必须给其他所有对象提供这一实例
下面看一下单例模式的写法
饿汉式
顾名思义,饿汉式,就是使用类的时候不管用的是不是类中的单例部分,都直接创建出单例类,看一下饿汉式的写法:
public class SingleObject {
 
    //创建 SingleObject 的一个对象
    private static SingleObject instance = new SingleObject();
 
    //让构造函数为 private,这样该类就不会被实例化
    private SingleObject(){}
 
    //获取唯一可用的对象
    public static SingleObject getInstance(){
        return instance;
    }
 
}
是否多线程安全:是
描述:这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
懒汉式:
public class Singleton {
    private static Singleton instance;
    private Singleton (){}
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
是否多线程安全:是
描述:这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。
 
双检锁
双检锁(Double Check Lock,简称DCL)的写法:
public class Singleton {
    private volatile static Singleton singleton;
    private Singleton (){} //防止外部创建实例 私有
    public static Singleton getSingleton() {
        if (singleton == null) {//第一个锁,如果没有实例
            synchronized (Singleton.class) {//第二个锁,如果没有任何线程创建Singleton实例  对象锁 - 若多个线程拥有同一个MyObject类的对象,则这些方法只能以同步的方式执行
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
线程A初次调用getSingleton()方法,判断singleton为null,进入同步代码块,此时线程切换到线程B,线程B调用getSingleton()方法,由于同步代码块外面的代码还是异步执行的,所以线程B走12行,判断singleton为null,等待锁。结果就是线程A实例化出了一个Singleton,释放锁,线程B获得锁进入同步代码块,判断此时singleton不为null了,并不实例化Singleton。这样,单例类就保证了在内存中只存在一份。
 
 

登记式/静态内部类:

这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
public class Singleton {
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton (){}
    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟第 1 种方式不同的是:第 1 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 1种方式就显得很合理。
 
 

枚举

public enum Singleton {
    INSTANCE;
    public void whateverMethod() {
    }
}
一般情况下,不建议使用懒汉方式,建议使用饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用登记方式。如果涉及到反序列化创建对象时,可以尝试使用枚举方式。如果有其他特殊的需求,可以考虑使用双检锁方式。
 
单例模式的好处
作为一种重要的设计模式,单例模式的好处有:
1、控制资源的使用,通过线程同步来控制资源的并发访问
2、控制实例的产生,以达到节约资源的目的
3、控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信

猜你喜欢

转载自www.cnblogs.com/rachael/p/10187329.html
今日推荐