设计模式
单件模式
定义
确保一个类只有一个实例,且提供该实例的全局访问方法。
分类
经典延迟单件模式
public class DelaySingleClass {
public static DelaySingleClass delaySingleClass;
private DelaySingleClass() {
}
public static DelaySingleClass getInstance() {
if (delaySingleClass == null) {
delaySingleClass = new DelaySingleClass();
}
return delaySingleClass;
}
}
把类的实例化延迟到了运行时即可。这是最经典和简单的单件模式的实现。
存在的是问题是:
如果是并发编程,则不同线程可能在同时执行getInstance()方法。这使得同时产生多个DelaySingleClass的实例。
无延迟单件模式
public class NotDelaySingleClass {
private static NotDelaySingleClass notDelaySingleClass = new NotDelaySingleClass();
private NotDelaySingleClass() {
}
public static NotDelaySingleClass getInstance() {
return notDelaySingleClass;
}
}
jvm会确保每次getInstance()都返回同一个实例对象。
同步获取-延迟单件模式
public class SyncSingleClass {
private static SyncSingleClass syncSignleClass;
private SyncSingleClass() {
}
public static synchronized SyncSingleClass getInstance() {
if (syncSignleClass == null) {
syncSignleClass = new SyncSingleClass();
}
return syncSignleClass;
}
}
保证多个线程同时执行getInstance()方法时仍然获取到唯一的实例对象。
从同步的做法上看:这里采用隐式持有对象锁的方式来对getInstance()方法进行同步,也可以采用显式的方式。
存在的问题:每个想要获取SyncSingleClass对象的线程都要被迫同步!大大降低了程序的运行效率。事实上,我们只需要确保该类第一次被实例化之前new对象的语句是同步的即可。
双重判断-同/异步获取-延迟单件模式
public class DoubleSyncSingleClass {
// volatile多余
public volatile static DoubleSyncSingleClass doubleSyncSingleClass;
private DoubleSyncSingleClass() {
}
public static DoubleSyncSingleClass getInstance() {
if (doubleSyncSingleClass == null) {
synchronized (DoubleSyncSingleClass.class) {
if (doubleSyncSingleClass == null) {
doubleSyncSingleClass = new DoubleSyncSingleClass();
}
}
}
return doubleSyncSingleClass;
}
}
只在需要new 对象的时候才进行同步。
-
第一次非空判断是确认当前
主存
中doubleSyncSingleClass值是否为null,如果为null则需要创建对象,否则直接返回现有对象。 -
第二次非空判断是因为第二个及之后的执行该方法的线程可能已经在等待持有DoubleSyncSingleClass.class的锁,而第一个持有该锁的线程正在进行
创建对象
,当第一个线程创建对象完毕后,就会把对象的引用写入主存
中,此时doubleSyncSingleClass的值就已经改变了,即没有必要再创建对象了。
总体来说,没有必要使用volatile关键字确保doubleSyncSingleClass的可视性,因为synchronized已经保证了这一点。