最近在学习设计模式中的单例模式,写下此篇总结,权当笔记。
单例模式是一种常见的设计模式。为了保持程序运行时jvm中单例对象只存在一个,这样的方式有这些优点:省去了繁多的new关键字的使用,节省了系统的开销,减轻了CG回收的压力,在有的系统中要求只能拥有一个实例,只有这样才能控制系统的核心逻辑不会错。
在使用单例模式时,可以使用如下例子的方法创建:
public class Single{
//使用static 修饰类的实例
private static Single sin = null;
//构造方法私有化,防止直接实例化
private Single(){}
//创建静态方法,返回本类的实例
public static Single getSingle(){
if(sin==null){
sin = new Single();
}
return sin;
}
}
以上例子可以满足基本的要求,但是如果考虑多线程时,只有上述例子的措施还是不够的。试想在线程A使用getSingle方法创建实例时,线程B也运行到了getSingle这个方法,都判断sin为null,也就都会创建sin这个实例。这对于使用final修饰的sin对象来说,是不符合java的语法的。为解决这个问题,我们想到使用synchronized关键自实现线程互斥。现将getSingle方法改造如下:
public static Single getSingle(){
synchronized(sin){
if(sin == null){
sin = new Single();
}
}
return sin;
}
使用关键字似乎可以完成期望,但是jvm在完成赋值和创建对象上是不同步的,也就是说在sin实例化的时候,jvm先申请了一个空内存,然后直接赋值给sin(这是jvm还没有对sin进行实例化)。这样sin就不null,线程B就不经过sin对象的实例化,但这是sin对象实际只是一个空的内存引用。这样线程B在后续的程序运行中就会出现问题。
其实比较完善且常使用的方式是这样的:
public class Single{
private static class SingleFactory{
private static Single sin = new Single();
}
public static getSingle(){
return SingleFactory.sin;
}
}
这个改进的例子原理是,在类加载时,加载过程中是线程互斥的。这样,就保证了实例过程中的线程互斥。