您的位置:首页 > 其它

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,而且还能防止反序列化导致重新创建新的对象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: