单例设计模式I (饿汉式)在开发中比较常见。而另一种写法,即懒汉式主要用于面试考试。
但因为感觉比较有趣,所以单独列出做个记录。
懒汉式:对象延迟加载。
思路:类存在时内部没有对象,只有当调用方法时才会在类中建立对象(比喻相当形象啊……到底是有多懒= =|||)。
特点:
对象延迟加载;
容易存在安全隐患;
先来最终解决方案的看代码:
class Single
{
private static Single s= null;
private Single(){}
public static Single getInstance()
{
if(s== null)
{
synchronized(Single.class)
{
if(s== null)
{
s= new Single();
}
}
}
return s;
}
}
—————————————————(我是现在很饿的分割线)——————————————————
class Single
{
private static Single s= null;
private Single(){}
public static Single getInstance()
{
if(s== null)
s= new Single();
return s;
}
}
主要问题出在 getInstance( ) 方法中;
因为CPU的一个核心在同一时刻只能处理一个任务,为了高效处理多个任务,它会在极小的时间单位里随机进行不同任务的之间的切换。
现在假设程序A调用了 getInstance( ) 方法,并运行到了这条语句:
if(s== null)
此刻CPU却切换到了其他任务,于是程序A就挂在了此处。
于此同时,程序B也调用了 getInstance( ) 方法,进入并执行了语句:
if(s== null)
s= new Single();
因为此前程序A在创建对象之前由于CPU切换而挂停,所以此时程序B判断 s 的值为空是成立的,因此可以创立对象。
接着,CPU切换回来,程序A继续读下面的语句,注意此处A已经判断过 s 的值为空,所以依然可以创立对象。
于此,对象在类中的唯一性就已经被打破了。
解决方案为同步锁(其实并没有学到这里,只是老师单独讲解了一下并提到了这个,名称也许不正式)
public static Single synchronized getInstance()
{
if(s== null)
s= new Single();
return s;
}
在方法前加上同步锁 synchronized 可以使调用者在进入前进行判断,如果该方法没有人调用,就进入。否则不调用。
这样基本解决了对象在类中的唯一性问题,但却托慢了代码的运行效率。
因为每次调用前都会对同步锁进行判断,多次调用多次判断十分低效。
优化思想为双重判断,代码如下:
public static Single getInstance()
{
if(s== null)
{
synchronized(Single.class)
{
if(s== null)
{
s= new Single();
}
}
}
return s;
}
重新使用上面的例子:
首先程序A调用 getInstance( ) 方法,判断 s 值为空,进入然后对锁进行判断,锁内没有程序,进入。
如果此时CPU进行了切换导致程序A挂停,当程序 B 调用方法时,再进行判断同步锁时,则发现锁内有程序进入,即停止调用。
如果锁告知内部没有程序,即直接进入进行对象创建。
于此,不需要每次调用 getInstance( ) 方法时都判断一次同步锁,提高了代码的运行效率。