java 开发都知道再java 软件架构中有6种软件架构原则,23种设计模式,今天我们就来聊聊23种设计模式中的 单例模式。
今天我们主要看3类单例模式,其余的几种暂时先不看。
1、饿汉式
一个项目可能我们需要一些全局的配置文件,或者一个上下文一个项目可能只需要一个并且在项目启动的时候就初始化好了。这个时候饿汉式是比较好的选择
EagerSingleton.java
package single;
public class EagerSingleton {
private static EagerSingleton instance = new EagerSingleton();
private EagerSingleton(){}
public static EagerSingleton getInstance(){
return instance;
}
}
SingleTest.java
package single;
public class SingleTest {
public static void main(String[] args) throws InterruptedException {
new Thread(() ->{
EagerSingleton eagerSingleton = EagerSingleton.getInstance();
System.out.println(eagerSingleton);
}).start();
new Thread(() ->{
EagerSingleton instance = EagerSingleton.getInstance();
System.out.println(instance);
}).start();
}
single.EagerSingleton@7db811c1
single.EagerSingleton@7db811c1
2、懒汉式单例
LazySingleton.java
package single;
public class LazySingleton {
private static LazySingleton lazySingleton = null;
private LazySingleton(){}
public static LazySingleton getInstance(){
if(lazySingleton == null){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
SingleTest.java
package single;
/**
* @Description:
* @Author: bhh
* @Mail: sunjin@sudiyi.cn
* @Date: 2019/7/25
*/
public class SingleTest {
public static void main(String[] args) throws InterruptedException {
LazySingleton instance = LazySingleton.getInstance();
LazySingleton instance1 = LazySingleton.getInstance();
System.out.println(instance);
System.out.println(instance1);
new Thread(() ->{
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
}).start();
new Thread(() ->{
LazySingleton instance1 = LazySingleton.getInstance();
System.out.println(instance1);
}).start();
Thread.sleep(3000);
}
}
这种懒汉式单例其实是存在线程安全问题的,再多线程环境下可能会违背单例的原则那么我们有其它方式改进吗,当然加锁是可以的。
SynchronizedLazySingleton.java
package single;
public class SynchronizedLazySingleton {
private static SynchronizedLazySingleton synchronizedLazySingleton = null;
private static Object lock = new Object();
private SynchronizedLazySingleton(){}
public static SynchronizedLazySingleton getInstance(){
synchronized (lock) {
if(synchronizedLazySingleton == null){
synchronizedLazySingleton = new SynchronizedLazySingleton();
}
return synchronizedLazySingleton;
}
}
}
这样就是线程安全的了,但是会存在性能问题,synchronizedLazySingleton 只会再第一次为空,但是我们每次进来都需要获取锁,都需要等待锁的释放。接下来我看再看一种 double check 的模式
DoubleCheckLazySingleton.java
package single;
public class DoubleCheckLazySingleton {
private static DoubleCheckLazySingleton doubleCheckLazySingleton = null;
private static Object lock = new Object();
private DoubleCheckLazySingleton(){}
public static DoubleCheckLazySingleton getInstance(){
if(doubleCheckLazySingleton == null){
synchronized (lock) {
if(doubleCheckLazySingleton == null) {
doubleCheckLazySingleton = new DoubleCheckLazySingleton();
return doubleCheckLazySingleton;
}
}
}
return doubleCheckLazySingleton;
}
}
这样改完以后是不是觉的问题了的线程安全的文件也解决了,好了,其实时候还有问题的,我们知道 jvm 为了提高性能,看能会对没有依赖关系的几行代码再不影响结果的情况下做指令重排序,这样就看能导致一些问题了,
new DoubleCheckLazySingleton(); 这个操作对我们来看是一行代码,其实jvm会分为3步,
1 分配内存,2 初始化成员变量,3 栈指针指向堆。但是这三步可能会重排序,1->3->2 这样的话在多线程情况下发生空指针,为了进一步优化我们 会在 private static volatile DoubleCheckLazySingleton doubleCheckLazySingleton = null;
加一个关键字volatile 这样懒汉式的单例就比较完美了,volatile的作用会禁止jvm多加了关键字的指令重排序,当然它是怎么实现的我们会在后面的章节做简绍。
到此看能我们觉的为啥实现一个单例这么复杂,要考虑的这么多,其实还有优雅的写法 内部类的方式
3、内部类实现单例
UpperSingleton.java
package single;
public class UpperSingleton {
private UpperSingleton (){}
public static UpperSingleton getInstance(){
return InnerInstance.instance;
}
public static class InnerInstance{
private static UpperSingleton instance = new UpperSingleton();
}
}
现在觉得是不是感觉不一样了呢,懒汉式单例是在用的时候才去创建对象这样比较节省内存,在一些工具类中如果 要用到单例 我比较推荐懒汉式,
到此不知道您对单例模式是不是有个大体的理解了呢,今天的分享就到这个,下次我们分享其它的设计模式。如果您有自己的理解欢迎和我交流 1129098627@qq.com