单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例
Singleton Pattern: Ensure a class has only one instance, and provide a global point of access to it
?
优点:减少内存开支;减少系统性能开销;避免对系统资源的多重占用;优化和共享资源访问
缺点:不易扩展(?);不利于测试,为完成的单例模式不能使用mock虚拟;单例模式与单一职业原则有冲突
?
(PS:一个典型应用,spring中,每个Bean默认都是单例的)
?
Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。(事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在)
?
饿汉式:无论是否使用均实例化对象
?
public class SingletonEager {
private static final SingletonEager instance = new SingletonEager();
public static SingletonEager getInstance() {
return instance;
}
private SingletonEager() {
}
}
??
懒汉式(最常用):在需要用到的时候才进行第一次初始化
?
public class SingletonLazy {
private static SingletonLazy instance = null;
public static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
private SingletonLazy() {
}
}
??
?
上述懒汉式存在一个问题,同步问题,多个线程同时访问该方法时,可能会实例化出多个对象,所以:
懒汉式*改1 : 给getInstance方法增加synchronized关键字
?
public class SingletonLazy1 {
private static SingletonLazy1 instance = null;
public synchronized static SingletonLazy1 getInstance() {
if (instance == null) {
instance = new SingletonLazy1();
}
return instance;
}
private SingletonLazy1() {
}
}
?
?
承上,synchronized方法会大大拖慢代码性能,因此:
懒汉式*改2(这种形式也叫双重锁形式):将synchronized关键字放到if之后
?
public class SingletonLazy2 {
private static SingletonLazy2 instance = null;
public static SingletonLazy2 getInstance() {
if (instance == null) {
synchronized (SingletonLazy2.class) {
if (instance == null) {
instance = new SingletonLazy2();
}
}
}
return instance;
}
private SingletonLazy2() {
}
}
??
?
小结:以上就是单例模式的几种基本写法,具体使用哪一种,就看具体使用环境的要求严格程度了。
扩展模式:固定数量的单例
?
public class SingletonMulti {
//最大实例数量
private static int maxNumOfSingleton = 2;
//实例名称列表
private static ArrayList<String> nameList = new ArrayList<String>();
//实例list
private static ArrayList<SingletonMulti> singletons = new ArrayList<SingletonMulti>();
private static int countNumOfSingleton = 0;
static {
for (int i = 0; i < maxNumOfSingleton; i++) {
singletons.add(new SingletonMulti("Sample " + i));
}
}
private SingletonMulti() {
}
private SingletonMulti(String name) {
nameList.add(name);
}
//随机获取一个单例对象
public static SingletonMulti getInstance() {
Random random = new Random();
countNumOfSingleton = random.nextInt(maxNumOfSingleton);
return singletons.get(countNumOfSingleton);
}
}
?
?
?补充:最后还有一种叫做登记式单例模式,这个模式似乎在spring里用于管理单例,本人不是特别理解,在此不做详述,以后有机会看了spring的源码可能会对此做进一步探讨。有兴趣的可以看一下参考内容中列到的第3个链接单例模式详解
?
? ? 关于这个问题,国内外都有各种讨论,对于内存分配的一些细节问题,在下实在才疏学浅,搞不清楚java底层的内存分配规则,只能结合我看到的内容,谈一下自己对用法上一些直观的看法:
?
参考内容:
?
?
原文:http://jy03100000.iteye.com/blog/2210274