java单例模式实例_java单例模式讲解和示例

本文深入探讨了Java中的单例模式实现,包括懒汉式、饿汉式以及双重检查锁定(DCL)等方法,并分析了并发环境下可能出现的问题,如指令重排序导致的线程安全问题。此外,还介绍了枚举单例的简洁与安全,以及如何通过序列化和反射破坏单例的唯一性。最后,讨论了不同单例实现的优缺点和适用场景。
摘要由CSDN通过智能技术生成

它是由一下3个指令完成的:

给 instance 分配内存

调用 Singleton 的构造函数来初始化成员变量

将instance对象指向分配的内存空间地址

JVM编译时进行指令重排序可能打乱上面三个指令的执行顺序,也就是说可能先直行来1,3然后执行2。那么有这么一种情况当执行好1和3,instance不为null,新进入的线程在判断第一个null时就会直接返回一个没有执行2步骤的实例,如此就有不符合期望了。这的确是个经典的场景。

额外阅读

如果我们在实例初始化后,将第三步,分开写,似乎可以解决这个问题,代码如下:

public staticSingleton getInstance() {if (instance == null) {synchronized (Singleton.class) {

Singleton temp=instance;if (temp == null) {synchronized (Singleton.class) {

temp= newSingleton();

}

instance=temp;

}

}

}returninstance;

}

volatile关键字事实上是对编译时的重排序进行了屏障。具体各家说法可以阅读下面的文章:

扩展阅读

由上可以感受到,在累加载时就初始化好实例,会有很多需要考虑的东西,那么如果在编译阶段就实例化好,如此就可以避免并发带来的问题。

那就是所谓的饿汉式单例:

public classSingleton{//类加载时就初始化

private static final Singleton instance = newSingleton();privateSingleton(){}public staticSingleton getInstance(){returninstance;

}

}

当然这样做自然有自己的弊端,就是这个单例在没有被使用到的时候就已经需要实例化出来,如此就会占用无谓占用内存,如果这个实例初始化复杂占用资源,而实际未必会使用就比较尴尬了。

或者说,这种方式实例化将无法实现依赖外部参数实例化的场景。

还有一种推荐写法:

public classSingleton {private static classSingletonHolder {private static final Singleton INSTANCE = newSingleton();

}privateSingleton (){}public static finalSingleton getInstance() {returnSingletonHolder.INSTANCE;

}

}

还有一种大师推荐的写法,有没有很高大上:

public enumEasySingleton{

INSTANCE;

}

我们来看看如何破坏单例:

1,序列化与反序列化

当然我们前面写的代码不需要序列化和反序列化,就没有这个问题了,只是说送这个方面我们考虑如何破坏它,参考如下例子:

public class Singleton implementsSerializable{private volatile staticSingleton singleton;privateSingleton (){}public staticSingleton getSingleton() {if (singleton == null) {synchronized (Singleton.class) {if (singleton == null) {

singleton= newSingleton();

}

}

}returnsingleton;

}

}public classSerializableDemo1 {//为了便于理解,忽略关闭流操作及删除文件操作。真正编码时千万不要忘记//Exception直接抛出

public static void main(String[] args) throwsIOException, ClassNotFoundException {//Write Obj to file

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile"));

oos.writeObject(Singleton.getSingleton());//Read Obj from file

File file = new File("tempFile");

ObjectInputStream ois= new ObjectInputStream(newFileInputStream(file));

Singleton newInstance=(Singleton) ois.readObject();//判断是否是同一个对象

System.out.println(newInstance ==Singleton.getSingleton());

}

}//false

2,反射

public classSingleton {public static final Singleton INSTANCE = newSingleton();privateSingleton() {

}publicSingleton getInstance() {returnINSTANCE;

}public static void main(String[] args) throwsException {//反射机制破坏单例模式

Class clazz = Singleton.class;

Constructor c=clazz.getDeclaredConstructor();//反射机制使得private方法可以被访问!!!

c.setAccessible(true);//判断反射生成的对象与单例对象是否相等

System.out.println(Singleton.INSTANCE ==c.newInstance());

}

}

破坏单例的原理就是,不走构造函数即可产生实例的方式,因为我们只关闭了构造函数。

至此,对java单例有一个比较全面的认识,牵涉到大量知识点,需要继续挖掘。

让我们继续前行

----------------------------------------------------------------------

努力不一定成功,但不努力肯定不会成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值