单例设计模式
来源:互联网 发布:mysql insert慢 io 编辑:程序博客网 时间:2024/05/21 11:26
–饿汉式–
饿汉式是线程安全的, 因为在创建类的同时就创建好了一个静态对象, 以后不会改变
public class Singleton {
//私有化构造函数private Singleton() {}private static Singleton instance = new Singleton();//静态工厂方法public static Singleton getInstance() { return instance;}
}
–懒汉式–
懒汉式是需要的时候才创建对象, 所以存在多线程安全问题
1.懒汉式第一种(未决解多线程安全)
public class Singleton {
private Singleton() {}private static Singleton instance = null;//静态工厂方法public static Singleton getInstance() { if(instance == null) { instance = new Singleton(); } return instance;}
}
2.懒汉式第二种(决解线程安全问题)(但其实还存在细微的线程安全问题)
public class Singleton {
private Singleton() {}private static Singleton instance = null;public static Singleton getInstance() { //这里再加一个判断, 提高性能, 避免线程阻塞 if (instance == null) { //加锁 synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance;}
}
站在JVM编辑器的角度看看, 这里涉及到 JVM指令重排
指令重排是什么呢?
参考博客 http://blog.csdn.net/pxg943055021/article/details/69525035
在举个简单的例子例如 instance = new Singleton()
就会被编辑器便编译成如下JVM指令
memory = allocate(); //1. 分配对象的内存空间
ctorInstance(memory); //2. 初始化对象
instance = memory; //3. 设置instance指向刚分配的内存地址
上面操作2依赖于操作1,但是操作3并不依赖于操作2, 所以JVM是可以针对它们进行指令的优化重排序的, 经过重排序后如下
memory = allocate(); //1. 分配对象的内存空间
instance = memory; //3. 设置instance指向刚分配的内存地址
ctorInstance(memory); //2. 初始化对象
假设有线程A, B
当线程A执行完1, 3时 instance对象还未被完成初始化, 但instance不在是null, 假如此时B线程抢到执行权
判断if(instance == null)为false, 就会直接返回一个未经过上述2过程的对象, 是一个未被初始化的对象, 就会发生问题
所以下面给出决解方案
3.懒汉式第三种
这里引入volatile
public class Singleton {
private Singleton() {}//这里加入volatileprivate static volatile Singleton instance = null;//静态工厂方法private static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance;}
}
什么是volatile
volatile是一个类型修饰符(type specifier),就像大家更熟悉的const一样,它是被设计用来修饰被不同线程访问和修改的变量。volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
volatile可以防止指令重排
简单说就是当线程执行instance = new Singleton()
能保证JVM编译器就会按照下面的顺序来执行
memory = allocate(); //1:分配对象的内存空间
ctorInstance(memory); //2:初始化对象
instance = memory; //3:设置instance指向刚分配的内存地址
此时instance要么指向null 要么就是完整的初始化状态
4.懒汉式第四种
用静态内部类实现单例模式
public class Singleton {
private Singleton() {}private static class LazyHolder { private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() { return LazyHolder.INSTANCE;}
}
该对象初始化, 并不是在类Singleton被加载时初始化, 而是在调用getInstance()方法时初始化,也是懒加载的一种
该方式既实现了线程安全,又避免了同步带来的性能影响
以上所有方法都无法防止使用反射来重复构建对象
5.第五种(推荐)
使用枚举实现单例, 可以防止反射构造对象, 还可以保证线程安全 只是该方法是非懒加载
该单例对象是在枚举类被加载的时候就进行初始化的
public enum Singleton {
INSTANCE;
}
public static void main(String[] args) {
Singleton s1 = Singleton.INSTANCE;
Singleton s2 = Singleton.INSTANCE;
System.out.println(s1.equals(s2)); //true
}
- 设计模式--单例
- 单例设计模式
- 设计模式----单例
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 设计模式-单例
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 【Scikit-Learn 中文文档】预测目标 (y) 的转换
- 转载http://blog.csdn.net/liaoxinmeng/article/details/
- 为什么需要知识图谱?什么是知识图谱?——KG的前世今生
- 【Scikit-Learn 中文文档】数据集加载工具
- 哥德巴赫猜想
- 单例设计模式
- 实时监听input中值得变化
- 将一字符串向左移动k位
- kafka线程模型之三 QuotaManager
- HDoj 1031 Design T-shirt(题目很绕的水题)
- Java语言规范基于JavaSE9 第七章 包和模块(三)
- 哥德巴赫猜想(升级版)
- 趣图丨PHP果然是世界上最好的语言!
- WRTnode-Windows的putty连接