重修设计模式-单例模式:
0.修前叨叨:
设计模式,一共23个.从今天开始,我打算每天抽空重修一个,方便自己的记忆以及更深入的理解.
单例模式算是接触比较多的一个东西.以前,用的比较多.对于这个东西,起初觉得比较简单,只是对于重量级实例的封装容器的方式.现在,回头再来看,发现细节远不止这些.
1.概念:
单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
2.深挖细节:
单例模式,会在多种场景下使用,所需的实现方式和要求也各不相同.
2.1饿汉法:
public class Singleton {
private static Singleton = new Singleton();
private Singleton() {}
public static getSignleton(){
return singleton;
}
}
故名思议,一看就是没吃饱.所以,在jvm启动的时候通过在class初始化的连接过程中进行赋值.其他调用的地方就可以放心使用,完全不用害怕线程的影响啦.但是,这个方式如果遇到需要延迟加载的类,那就不太合适了.这种需要延迟加载的单例,就需要下面的懒汉来处理.
2.2懒汉法:
2.2.1线程不安全:
public class Singleton {
private static Singleton singleton = null;
private Singleton(){}
public static Singleton getSingleton() {
if(singleton == null) {//check&pro 明显的线程不安全点
singleton = new Singleton();
}
return singleton;
}
}
上面的代码,在单线程使用的情况下是没有任何问题的.但是,被多线程使用的话,这个singleton对象会有被多次初始化的风险.因为,我们使用懒汉式的单例模式必然是有一个类加载比较消耗资源,我们连开机的时候都不想加载他.当被多次初始化的话,会对程序的性能产生很大的影响.
2.2.2线程安全:
public class Singleton {
private static Singleton singleton = null;
private Singleton() {
}
public static Singleton getSingleton() {
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
return singleton;
}
}
咯,上面的代码里面加了个synchronized关键字,将本对象锁住以后就可以在判断的时候避免发生脏数据干扰,同时,由于单例模式的对象初始化一般都比较耗时,这时如果有其他线程也来获取的话可以阻塞住,直到单例创建完成以后再返回对象.但是,这也产生了新的问题,就是多线程跑到这里都变成了单线程了.接下来我们就会介绍一种兼顾线程安全和性能的实现思路
2.2.3线程安全和性能兼顾的:
public class Singleton {
private static volatile Singleton singleton = null;
private Singleton(){}
public static Singleton getSingleton(){
if(singleton == null){
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
上述代码,会在多线程获取单例时先判断是否有对象,如果有就不会去同步直接返回.如果没有就会去走同步check和处理.这样的处理方式兼顾了线程安全和单例实例生成后的多线程性能.但是,整个对象是存在的判断是基于volatile这个关键字的可见性和不可变性.经过查询的资料,volatile的代码不可变性实际上是在jdk1.5以后才真正的实现的,所以这个写法在jdk1.5及其以下的版本中使用是线程不安全的.
2.3静态内部类&枚举法
由于这2种方法具体的原理我不太熟悉,后面在更加深入了解原理后再补全.