ava单例模式的正确写法
2016-04-22 23:56
218 查看
一、懒汉式(线程不安全)
Java代码
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
介绍:线程不安全,在多线程情况下容易创建多个实例。
二、懒汉式(线程安全)
Java代码
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
介绍:虽然线程安全,但是不够高效。
三、双重检验锁
Java代码
public static Singleton getSingleton() {
if (instance == null) { //Single Checked
synchronized (Singleton.class) {
if (instance == null) { //Double Checked
instance = new Singleton();
}
}
}
return instance ;
}
介绍:双重检验锁机制比懒汉式(线程安全)要高效很多,因为它不用对instance不为空的情况进行同步。这段代码看起来似乎很完美,但很可惜,还是存在问题。instance=new
Singleton() 这并非是一个原子性质的操作,事实上在JVM中做了如下三件事:
给instance分配内存
调用构造函数初始化成员变量
将instance对象指向分配的内存(此步骤完成instance即为非空)
但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。
四、双重检验锁(volatile)
Java代码
public class Singleton {
private volatile static Singleton instance; //声明成 volatile
private Singleton (){}
public static Singleton getSingleton() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
介绍:使用 volatile 的原因是可以保证线程在本地不会存有 instance 的副本,每次都是去主内存中读取。这里使用volatile的一个主要原因就是volatile可以禁止JVM指令重排序。
五、饿汉式
Java代码
public class Singleton{
//类加载时就初始化
private static final Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
介绍:这种方法非常简单,因为单例的实例被声明成 static 和 final 变量了,在第一次加载类到内存中时就会初始化,所以创建实例本身是线程安全的。缺点是它不是一种懒加载模式。比如 Singleton 实例的创建是依赖参数或者配置文件的,在 getInstance() 之前必须调用某个方法设置参数给它,那样这种单例写法就无法使用了。
五、静态内部类
Java代码
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
介绍:这种写法仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance()
之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。
六、枚举
Java代码
public enum EasySingleton {
INSTANCE;
EasySingleton(){
}
}
介绍:我们可以通过EasySingleton.INSTANCE来访问实例,这比调用getInstance()方法简单多了。创建枚举默认就是线程安全的,所以不需要担心double checked locking,而且还能防止反序列化导致重新创建新的对象。
Java代码
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
介绍:线程不安全,在多线程情况下容易创建多个实例。
二、懒汉式(线程安全)
Java代码
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
介绍:虽然线程安全,但是不够高效。
三、双重检验锁
Java代码
public static Singleton getSingleton() {
if (instance == null) { //Single Checked
synchronized (Singleton.class) {
if (instance == null) { //Double Checked
instance = new Singleton();
}
}
}
return instance ;
}
介绍:双重检验锁机制比懒汉式(线程安全)要高效很多,因为它不用对instance不为空的情况进行同步。这段代码看起来似乎很完美,但很可惜,还是存在问题。instance=new
Singleton() 这并非是一个原子性质的操作,事实上在JVM中做了如下三件事:
给instance分配内存
调用构造函数初始化成员变量
将instance对象指向分配的内存(此步骤完成instance即为非空)
但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。
四、双重检验锁(volatile)
Java代码
public class Singleton {
private volatile static Singleton instance; //声明成 volatile
private Singleton (){}
public static Singleton getSingleton() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
介绍:使用 volatile 的原因是可以保证线程在本地不会存有 instance 的副本,每次都是去主内存中读取。这里使用volatile的一个主要原因就是volatile可以禁止JVM指令重排序。
五、饿汉式
Java代码
public class Singleton{
//类加载时就初始化
private static final Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
介绍:这种方法非常简单,因为单例的实例被声明成 static 和 final 变量了,在第一次加载类到内存中时就会初始化,所以创建实例本身是线程安全的。缺点是它不是一种懒加载模式。比如 Singleton 实例的创建是依赖参数或者配置文件的,在 getInstance() 之前必须调用某个方法设置参数给它,那样这种单例写法就无法使用了。
五、静态内部类
Java代码
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
介绍:这种写法仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance()
之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。
六、枚举
Java代码
public enum EasySingleton {
INSTANCE;
EasySingleton(){
}
}
介绍:我们可以通过EasySingleton.INSTANCE来访问实例,这比调用getInstance()方法简单多了。创建枚举默认就是线程安全的,所以不需要担心double checked locking,而且还能防止反序列化导致重新创建新的对象。
相关文章推荐
- HDU 5671 Matrix——BestCoder Round #81(div.1 div.2)
- mysql数据库设计原则
- CodeForces 665A Buses Between Cities
- 第一章 JAVA入门_Learn Java for Android Development(第三版)
- 20150203+JS巩固与加强1-02
- 几种分割字符串实现方法的比较
- android toast使用总结
- 后续_Learn Java for Android Development (第三版)
- 乌云章华鹏:如何构建高效的安全运维服务平台
- 一个关于类数组中如何使用new
- mysql百万级别数据查询心得
- CodeForces 612D The Union of k-Segments
- 20150203+JS巩固与加强1-01
- (转载)Python学习–数据分析路线
- protobuf中文教程(第一篇)
- hdu1061Rightmost Digit
- 从程序员到CTO的Java技术路线图
- 【递推】【NOIP模拟】彩灯的问题 Lights
- 从源代码看TreeMap、HashMap、Hashtable、ConcurrentHashMap、LinkedHashMap特性
- 数据结构的二叉链表中序遍历