单例模式讨论

在之前的博文中曾经介绍过单例模式:

https://blog.csdn.net/timchen525/article/details/78244101

这里重新讨论们下单例模式,并且引入了一个基于枚举的单例模式。

分析如下代码:
**
 * 懒汉模式
 */
public class SingletonExample {

    // 私有构造函数
    private SingletonExample() {
    }

    // 单例对象
    private static SingletonExample instance = null;

    // 静态的工厂方法
    public static SingletonExample getInstance() {
        if (null == instance) {
            instance = new SingletonExample();
        }
        return instance;
    }
}

上述代码也叫作懒汉模式,即只在第一次被调用的时候才初始化, 不是线程安全的。不安全的原因:当两个线程同时运行到if(null == instance)则,同时进行instance = new SingleExample();即被实例化两次。
改进添加同步操作(synchronized):
public class SingletonExample {

    // 私有构造函数
    private SingletonExample() {
    }

    // 单例对象
    private static SingletonExample instance = null;

    // 静态的工厂方法
    public static synchronized SingletonExample getInstance() {
        if (null == instance) {
            instance = new SingletonExample();
        }
        return instance;
    }
}
上述添加了synchronized使得该方法同一时刻只能被一个线程访问。因此,这种改进后的代码是线程安全的。
进一步基于synchronized进行改进:
public class SingletonExample {

    // 私有构造函数
    private SingletonExample() {
    }

    // 单例对象
    private static SingletonExample instance = null;

    // 静态的工厂方法
    public static SingletonExample getInstance() {
        if (null == instance) { // B
            synchronized (SingletonExample.class) {
                if (null == instance) { // 双重检测机制
                    instance = new SingletonExample();  // A-3
                }
            }
        }
        return instance;
    }
}
分析:上述代码将同步synchronized移到判null之后,此时,可能有两个线程同时要去获取synchronized(SingletonExample.class),因此,在内部里面再次判断一次。这样写的好处,之后,null != instance可以直接return,而不用进行同步synchronized操作。
注意注意:上述 不是线程安全的。
instance = new SingletonExample()操作需要进行如下操作:
1)memory = allocate()分配对象的内存空间
2)ctorInstance()初始化对象
3)instance = memory设置instance的指向刚分配的内存
比如如下情况: JVM和CPU优化,发生了指令重排
1)memory = allocate()分配对象的内存空间
3)instance = memory设置instance的指向刚分配的内存
2)ctorInstance()初始化对象
比如:两个线程,线程B执行到if(instance == null) 此时,线程B执行到instance=new SingletonExample();而new的操作被CPU指令重拍,比如到 3),此时,instance == null判断不成立,变返回instance产生错误。
再次改进上述线程不安全的单例(添加volatile)( 线程安全写法,推荐 ):
volatile + 双重检测机制 = 实现线程安全的单例模式
public class SingletonExample {

    // 私有构造函数
    private SingletonExample() {
    }
    // 单例对象
    private volatile static SingletonExample instance = null;
    // 静态的工厂方法
    public static SingletonExample getInstance() {
        if (null == instance) {
            synchronized (SingletonExample.class) {
                if (null == instance) {
                    instance = new SingletonExample();
                }
            }
        }
        return instance;
    }
}
改进成 饿汉模式(线程安全的)
即单例模式在类加载时进行创建:
public class SingletonExample {

    // 私有构造函数
    private SingletonExample() {
    }
    // 单例对象
    private static SingletonExample instance = new SingletonExample();

    // 静态的工厂方法
    public static SingletonExample getInstance() {
        return instance;
    }
}
饿汉模式是 线程安全的,但是可能会引起程序初始化加载变慢,如果程序没有使用,则会造成资源浪费。
枚举模式实现线程安全( 最推荐,线程安全的 ):
public class SingletonExample {

    // 私有构造函数
    private SingletonExample() {
    }

    public static SingletonExample getInstance() {
        return Singleton.INSTANCE.getInstance();
    }

    private enum Singleton {
        INSTANCE;

        private SingletonExample singletonExample;

        // JVM保证这个方法绝对只调用一次
        Singleton() {
            singletonExample = new SingletonExample();
        }

        public SingletonExample getInstance() {
            return singletonExample;
        }
    }
}
JVM保证构造函数Single()被调用一次,因此是线程安全的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值