前言:
来源于《head first 设计模式》。当作读书笔记了,这次看的是第五章装单例模式。连续看了两章,下一章预告命令模式。单例模式看着简单,但其中的道理并不少。
单例模式的概念
用于确保一个类只有一个实例,并提供全局访问点。
由于过于简单吧,跳过日常举例子的环节,直接写出一步步演变的单例模式
经典单例模式
public class Singleton {
private static Singleton uniqueInstance;
//私有化构造方法
private Singleton() {}
//提供全局访问点
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// other useful methods here
public String getDescription() {
return "I'm a classic Singleton!";
}
上述实现方法属于延迟实例化(lazy instantiaze),利用private让构造方法私有化,static修饰getInstance提供全局访问。
不过把这个简单单例模式存在一定的弊端哦,就是在多线程模式的时候,会产生不同的实例变量。
例如:有两个线程。线程一执行getInstance进入到判断uniqueInstance是否为null,为null准备实例化,此时跳转到线程二执行,线程二判断uniqueInstance==null,也是为null,也进入了实例化过程。=。=这就gg公司了啊,线程二创建完实例化对象后,执行其他方法,而到线程一的时候又创建实例化对象覆盖,执行其他方法。此时可能会把单例模式对象的一些初始化数据又出初始化了。
那么只能引入同步咯或者先初始化
解决同步的单例模式
- 先初始化
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return uniqueInstance;
}
// other useful methods here
public String getDescription() {
return "I'm a statically initialized Singleton!";
}
}
直接在成员变量处让其初始化。。即类加载器加载此类的时候顺路初始化。这样就不会存在并发问题咯。
- 引入同步
public class Singleton {
private static Singleton uniqueInstance;
// other useful instance variables here
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// other useful methods here
public String getDescription() {
return "I'm a thread safe Singleton!";
}
}
其实就是加入synchronized关键字,迫使每次只能一个线程访问此方法。但同时也带来了另外一个问题。降低了性能。我们不难发现一个问题,多线程并发问题只存在于对象的初始化阶段,往后的每次调用不需要同步啊。直接获取已实例化的对象。这样子的话同步貌似就成为了一种累赘。
那么如何改进呢?
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
用volatile关键字,使uniqueInstance变量修改的时候每个线程都能够知道,不会出现脏读的现象。
判断为null后再进行同步。只有第一次初始化才会彻底执行完代码。初始化完成后,不会再进行同步的判断。
总结
单例模式的主要实现步骤就是1.私有化构造方法 2.提供访问点,加上注意多线程并发问题即可。