`

java单例模式的正确写法

阅读更多

一、懒汉式(线程不安全)

 

public class Singleton {
    private static Singleton instance;
    private Singleton (){}

    public static Singleton getInstance() {
     if (instance == null) {
         instance = new Singleton();
     }
     return instance;
    }
}

 

介绍:线程不安全,在多线程情况下容易创建多个实例。

 

 

二、懒汉式(线程安全)

 

public static synchronized Singleton getInstance() {
    if (instance == null) {
        instance = new Singleton();
    }
    return instance;
}

  

介绍:虽然线程安全,但是不够高效。

 

 

三、双重检验锁

 

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中做了如下三件事:

 

  1. 给instance分配内存
  2. 调用构造函数初始化成员变量
  3. 将instance对象指向分配的内存(此步骤完成instance即为非空)

但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。

 

四、双重检验锁(volatile)

 

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指令重排序。

 

五、饿汉式

 

public class Singleton{
    //类加载时就初始化
    private static final Singleton instance = new Singleton();
    
    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
}

 

 介绍:这种方法非常简单,因为单例的实例被声明成 static 和 final 变量了,在第一次加载类到内存中时就会初始化,所以创建实例本身是线程安全的。缺点是它不是一种懒加载模式。比如 Singleton 实例的创建是依赖参数或者配置文件的,在 getInstance() 之前必须调用某个方法设置参数给它,那样这种单例写法就无法使用了。

 

五、静态内部类

 

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 版本。

 

六、枚举

 

public enum EasySingleton {
	
	INSTANCE;
	
	EasySingleton(){
		
	}
	
}

 

介绍:我们可以通过EasySingleton.INSTANCE来访问实例,这比调用getInstance()方法简单多了。创建枚举默认就是线程安全的,所以不需要担心double checked locking,而且还能防止反序列化导致重新创建新的对象。

 

 

2
2
分享到:
评论
5 楼 lgh1992314 2018-08-07  
使用 volatile 的原因是可以保证线程在本地不会存有 instance 的副本,每次都是去主内存中读取。

这句有问题:
volatile 保证的是每次更新都及时的写入到主存。
4 楼 Nick_Wing 2016-07-25  
wosyingjun 写道
luowuhua16 写道
双重检验锁,在被锁的代码部分,为什么还要再次检验是否为null?如果代码执行到被锁的部分,说明还是null

因为可能有多个线程通过第一个null判断,如果不加第二个null判断的话会创建多个实例。

第一步的 Single Checked 我觉得没有必要呀,因为   synchronized (Singleton.class)这个已经锁住了其他线程对这块锁包围的代码块的访问,那肯定是等到当前线程实例化完成之后释放才能允许其他线程进行访问的,这边没有明白双重check的原因?求解
3 楼 wosyingjun 2016-04-25  
luowuhua16 写道
双重检验锁,在被锁的代码部分,为什么还要再次检验是否为null?如果代码执行到被锁的部分,说明还是null

因为可能有多个线程通过第一个null判断,如果不加第二个null判断的话会创建多个实例。
2 楼 luowuhua16 2016-04-22  
双重检验锁,在被锁的代码部分,为什么还要再次检验是否为null?如果代码执行到被锁的部分,说明还是null
1 楼 451914442 2016-04-21  
高手!66666

相关推荐

    java单例模式实例

    一个简单的java工程,包含注释,一目了然,其中包含了单例模式的所有实现方式,懒汉式,饿汉式,双重校验,枚举,静态内部类等方式实现单例。

    Java 单例模式 懒汉模式

    Java 单例模式 懒汉模式 //懒汉式 多线程中不可以保证是一个对象

    Java 单例模式.pptx

    作为对象的创建模式, 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 PPT详细了单例模式的实现和使用场景

    Java SE程序 单例模式

    Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式...

    Java单例模式的全面总结

    Java单例模式,其中:单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种

    java单例模式的例子

    java单例模式的例子java单例模式的例子java单例模式的例子

    JAVA单例模式的几种实现方法

    详细讲解了Java单例模式的几种实现方式,并有详细的示例配合讲解.

    深入Java单例模式浅析

    Java设计模式-单例模式(懒汉和恶汉)

    java单例模式

    java单例模式开发的7中写法,网上搜索的,可以看看

    Java 单例模式 工具类

    源码 博文链接:https://tianlihu.iteye.com/blog/747737

    Java单例模式设计

    代码中演示了Java设计模式中的单例模式,其中包括饿汉单例模式,懒汉单例模式以及序列化饭序列化单例模式。在实际的开发中,可以直接借鉴使用。

    Java单例模式(DOC)

    Java单例模式Java单例模式Java单例模式Java单例模式

    java 获取 配置文件 属性 单例模式 高效加载

    通过单例模式实例化获取propertyUtil 工具包实例,高效加载配置文件,java语言编写。通过单例模式实例化获取propertyUtil 工具包实例,高效加载配置文件,java语言编写。通过单例模式实例化获取propertyUtil 工具包...

    实用Java的单例模式,实用于Java学习者

    实用Java的单例模式,实用于Java学习者 单例模式 单例模式

    java Singleton单例模式

    java Singleton单例模式 java Singleton单例模式

    Java设计模式之单例模式

    目录 单例模式的概念 单例模式的要点 单例模式类图 单例模式归类 单例模式的应用场景 单例模式解决的问题 单例模式的实现方式 单例模式实现方式对比 单例模式的概念 单例模式,顾名思义就是只有一个实例,并且由它...

    单例模式java代码

    编写一个类LimitInstanceClass,该类最多可以实例化指定个数实例。实例的个数用配置文件InstanceLimit.cfg指定。例如,如果InstanceLimit.cfg的内容为2,则LimitInstanceClass最多可以同时存在2个对象。...

    java单例模式连接数据库源码

    java单例模式连接数据库源码.可以直接引用只需加入相应的mysql 或 oracle的驱动,修改源码的连接地址用户名及密码。

    java单例模式及实现

    java单例模式及实现

Global site tag (gtag.js) - Google Analytics