设计模式——单例模式

设计模式——单例模式

单例模式:如果程序运行过程中,某个类的实例只有唯一的一份,那么可以说这个类就是单例的,相应的设计模式称谓单例模式。这种模式涉及到一个单一的类,该类自己负责创建自己的实例,同时必须确保这运行过程中只能有一个实例被创建。

单例模式的实现方式有:饿汉式,懒汉式,双重校验式,静态内部类方式,枚举方式,下面具体每一种方式的实现代码并分析优劣。

1-饿汉式

/**
 * 单例模式:饿汉式,天然线程安全,在初始化时立即加载这个对象
 */
public class Singleton1 {
    //0-私有化构造器
    private Singleton1(){}


    //1-类初始化时立即加载对象
    private static Singleton1 singleton = new Singleton1();

    //2-静态方法提供单例对象
    public static Singleton1 getSingleton(){
        return singleton;
    }
}

饿汉式:天然线程安全,没有加锁,效率较高,在类加载的时候就初始化实例,可能会产生垃圾对象,没有达到懒加载的效果。

测试代码:

@Test
public void testSingleton1(){
    Singleton1 singleton1 = Singleton1.getSingleton();
    Singleton1 singleton2 = Singleton1.getSingleton();

//断言两者是同一对象
    Assert.assertTrue(singleton1 == singleton2);
}

 

2-懒汉式

/**
 * 懒汉式,在需要使用对象时在new出来,有线程安全问题
 * 资源利用率提高了,但是每次调用的时候都要同步,并发效率低了
 */
public class Singleton2 {
    //0-私有化构造器
    private Singleton2(){
        //多次调用直接抛出异常,确保单例
        if(null != singleton){
            throw new RuntimeException("单例不允许创建多个对象");
        }
    }

    //1-设置私有静态属性
    private static Singleton2 singleton;

    //2-提供获取实例方法,加synchronized关键字则可以保证线程安全
    public static synchronized Singleton2 getSingleton(){
        if(singleton == null){
            singleton = new Singleton2();
        }
        return singleton;
    }
}

 

懒汉式:需要加锁进行同步才能保证线程安全,效率较低,实现了懒加载,在调用的时候才会校验是否需要加载,如果已经加载了则直接返回实例对象,否则调用构造器方法进行初始化并返回。

测试代码:

@Test
public void testSingleton2(){
    Singleton2 singleton1 = Singleton2.getSingleton();
    Singleton2 singleton2 = Singleton2.getSingleton();
    //断言两者是同一对象
    Assert.assertTrue(singleton1 == singleton2);
}

 

3-双重校验方式

/**
 * 双重检验锁方式:在静态获取单例实例时做了两次判断,且采用synchronized关键字确保单例
 */
public class Singleton3 {
    //0-私有化构造器
    private Singleton3(){}

    //1-设置私有静态属性,volatile关键字保证数据在多个线程间修改是可见的
    private volatile static Singleton3 singleton;

    //2-提供获取实例方法,双重检验,加synchronized关键字保证线程安全
    public static Singleton3 getSingleton(){
        if(null == singleton){
            synchronized (Singleton3.class){
                if(null == singleton){
                    singleton = new Singleton3();
                }
            }
        }
        return singleton;
    }
}

双重检验方式:采用双锁机制,在多线程下也能有较好的性能。

测试代码:

@Test
public void testSingleton3(){
    Singleton3 singleton1 = Singleton3.getSingleton();
    Singleton3 singleton2 = Singleton3.getSingleton();
    //断言两者是同一对象
    Assert.assertTrue(singleton1 == singleton2);
}

 

Spring中也是采用这种方式确保bean在容器中的唯一性(必须在<bean>中没有定义scope的值或者scope属性为singleton)代码如下所示:

DefaultSingletonBeanRegistry.class

 

protected Object getSingleton(String beanName, boolean allowEarlyReference) {

    Object singletonObject = this.singletonObjects.get(beanName);

    if(singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {

        Map var4 = this.singletonObjects;

        synchronized(this.singletonObjects) {

            singletonObject = this.earlySingletonObjects.get(beanName);

            if(singletonObject == null && allowEarlyReference) {

                ObjectFactory singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);

                if(singletonFactory != null) {

                    singletonObject = singletonFactory.getObject();

                    this.earlySingletonObjects.put(beanName, singletonObject);

                    this.singletonFactories.remove(beanName);

                }

            }

        }

    }

    return singletonObject != NULL_OBJECT?singletonObject:null;

}

4-静态内部类方式

/**
 * 静态内部类实现方式:
 * 1、外部类没有static属性,不会像饿汉式那样立即加载对象
 * 2、只有真正调用getSingleton(),才会加载静态内部类,加载类时是线程安全的
 *    只能被赋值一次
 * 3、兼备了并发高效调用和延迟加载的优势
 */
public class Singleton4 {
    //0-私有化构造器
    private Singleton4() {
    }

    //1-定义一个静态内部类,持有一个单例实例对象
    private static final class Singleton4Holder {
        private static final Singleton4 singleton = new Singleton4();
    }

    //2-获取单例实例方法
    public static Singleton4 getSingleton() {
        return Singleton4Holder.singleton;
    }
}

静态内部类方式:利用了静态域延迟初始化,饿汉式是在类加载的时候就初始化实例,但是静态内部类的方式必须在Singleton4Holder被主动使用的时候才会加载实例,线程安全,只适用于静态域的情况。

测试代码:

@Test
public void testSingleton4(){
    Singleton4 singleton1 = Singleton4.getSingleton();
    Singleton4 singleton2 = Singleton4.getSingleton();
    //断言两者是同一对象
    Assert.assertTrue(singleton1 == singleton2);
}

 

5-枚举方式

/**
 * 枚举方式:
 */
public enum Singleton5 {
    INSTANCE;
}

测试代码:

@Test
public void testSingleton5(){
    Singleton5 singleton1 = Singleton5.INSTANCE;
    Singleton5 singleton2 = Singleton5.INSTANCE;
    //断言两者是同一对象
    Assert.assertTrue(singleton1 == singleton2);
}

枚举方式:实现单例的最佳方式,自动支持序列化机制,绝对防止多次实例化对象,而且可以防止反序列化重新创建对象,从JDK1.5之后才有enum特性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值