全世界人民都知道单例设计模式中类的定义分为懒汉式和饿汉式两种,然而今天并不是要把它们做横向比较。实际上,不论饿汉式类的代码看起来有多么美轮美奂,在实际开发中它的效率总是不如懒汉式的。然而在笔试和面试中懒汉式的出镜率可以说是比饿汉式不知道高到哪里去了,因此把它完全弄懂应该是十分有必要的。
饿汉式:
class Single1
{
int num = 1;
private static Single1 single1 = new Single1();
private Single1(){}
static Single1 getInstance1()
{
return single1;
}
}
最简单的懒汉式:
class Single2
{
int num = 2;
private static Single2 single2 = null;
private Single2(){}
static Single2 getInstance2()
{
//如果两个线程同时访问,有可能会生成两个实例,不符合单例模式的要求
if(single2 == null)
single2 = new Single2();
return single2;
}
}
然而这样的类定义实际上是线程不安全的。正如注释上说的,因为线程的不确定性无法确认它们在访问Single2 = new Single2();时是否已经有一个Single2的实例生成。在面试中如果写出这样一个代码,往往会被追问如果改进能让它线程安全。根据一般思路,我们对代码做出如下改进:
class Single2
{
int num = 2;
private static Single2 single2 = null;
private Single2(){}
static Object obj = new Object(); //创建同步锁对象
static Single2 getInstance2()
{ synchronized(obj) //同步锁,确保某一时刻只有一个线程能够访问内部模块
{
if(single2 == null)
single2 = new Single2();
return single2;
}
}
}
代码经过以上的改进可以解决线程不安全问题,然而此时可能又会被问到:这样虽然能解决线程不安全问题,但是同时却降低了程序的效率,如何改进代码?其实在外层再添加一个条件判断语句就可以了。
class Single2
{
int num = 2;
private static Single2 single2 = null;
private Single2(){}
static Object obj = new Object();
static Single2 getInstance2()
{
if(single2 == null)
{
synchronized(obj)
{
if(single2 == null)
single2 = new Single2();
}
}
return single2;
}
}
完美了,估计没有什么好问的了,测试一下。
public class Single
{
public static void main(String[] args)
{
Single1 s1 = Single1.getInstance1();
Single2 s2 = Single2.getInstance2();
System.out.println(s1.num+".."+s2.num);
}
}
输出结果:
1..2
没毛病!