最近闲来无事,再次的走进java设计模式。在再次看到单例设计模式时,对它有了更深层次的了解。下面我们来总结一下单例设计模式。
单例就是所谓的一个类就是只有一个实例。因此我们得出单例设计模式的定义:类只有一个实例,并且提供一个全局访问点。
首先我们来说说问什么我们需要单例模式。我们先举个巧克力工厂生产巧克力的简单例子。
1 public class ChocalaterBoiled{ 2 private boolean empty; //锅炉是否为空 3 private boolean boiled; //液体是否已经沸了 4 5 public boolean isEmpty(){ return empty;} 6 7 public boolean isBoiled(){ return boiled;} 8 9 public void fill(){ //锅炉中添加未沸的液体 10 if(isEmpty() && !isBoiled()){ 11 empty=false; 12 } 13 } 14 15 public void drain(){ //液体沸了后,倒出液体 16 if(!isEmpty() && isBoiled()){ 17 empty=true; 18 boiled=false; 19 } 20 } 21 22 public void boiled(){ 23 if(!isEmpty() ){ 24 boiled=true; 25 } 26 } 27 }
如果,我们实例化多个对象,这回产生什么情况了?不同对象状态有可能不一样,系统都乱套了。所以单例模式存在是有必要的。单例设计模式是用来管理公共资源的。所以我们需要将它设计为单例。
public class ChocalaterBoiled{ private boolean empty; //锅炉是否为空 private boolean boiled; //液体是否已经沸了 private static self; private ChocalaterBoiled(){} //私有构造方法 public static ChocalaterBoiled getInstance(){ //全局访问点 if(self==null) self=new ChocalaterBoiled(); retrun self; } public boolean isEmpty(){ return empty;} public boolean isBoiled(){ return boiled;} public void fill(){ //锅炉中添加未沸的液体 if(isEmpty() && !isBoiled()){ empty=false; } } public void drain(){ //液体沸了后,倒出液体 if(!isEmpty() && isBoiled()){ empty=true; boiled=false; } } public void boiled(){ if(!isEmpty() ){ boiled=true; } } }
是不是这样设计就可以实现了单例设计模式呢?在多线程中,这样实现是肯定有问题的。可问题有在哪儿呢?
问题出在我们第一次访问全局访问点getInstance()这儿。我们假设我两个线程,访问getInstance()。并且在第一个线程还没给静态字段实例化对象的时候第二个线程也执行到了if判断语句,并且执行了判断。在此时第二个线程挂起,起一个线程急需执行,它赋值给字段,并且调用fill()。不久后第二个线程被唤醒了,它也给静态字段赋予新对象,并且调用fill()方法,这样一来就产生过了两个对象。结果是浪费了一次导入的巧克力原料。这种可能是会发生的。
那怎样才能避免违背单例的原则了?
问题产生的原因是多线程。当然到多线程中找解决方案是肯定没问题的。我们可以同步getInstance()。
public class ChocalaterBoiled{ private boolean empty; //锅炉是否为空 private boolean boiled; //液体是否已经沸了 private static self; private ChocalaterBoiled(){} //私有构造方法 public static Sychronized ChocalaterBoiled getInstance(){ //全局访问点 if(self==null) self=new ChocalaterBoiled(); retrun self; } public boolean isEmpty(){ return empty;} public boolean isBoiled(){ return boiled;} public void fill(){ //锅炉中添加未沸的液体 if(isEmpty() && !isBoiled()){ empty=false; } } public void drain(){ //液体沸了后,倒出液体 if(!isEmpty() && isBoiled()){ empty=true; boiled=false; } } public void boiled(){ if(!isEmpty() ){ boiled=true; } } }
这样我们就解决了,在多线程环境下产生多个对象的问题。但是,大家知道,同步方法是很影响性能的。它可以降低性能的100倍。如果我们对性能要求很高的话,这种方式是不可取的。因为在第一次获取全局访问点之后,就不可能在出现上面说到的情况了。那我们如何来改进设计了?
一种很简单的方法叫“急切实例化”,即在我们声明静态字段的时候,初始化对象。这样就可以避免了多线程的问题。
public class ChocalaterBoiled{ private boolean empty; //锅炉是否为空 private boolean boiled; //液体是否已经沸了 private static self=new ChocalaterBoiled(); private ChocalaterBoiled(){} //私有构造方法 public static ChocalaterBoiled getInstance(){ //全局访问点 retrun self; } public boolean isEmpty(){ return empty;} public boolean isBoiled(){ return boiled;} public void fill(){ //锅炉中添加未沸的液体 if(isEmpty() && !isBoiled()){ empty=false; } } public void drain(){ //液体沸了后,倒出液体 if(!isEmpty() && isBoiled()){ empty=true; boiled=false; } } public void boiled(){ if(!isEmpty() ){ boiled=true; } } }
当然,我们还有一种更好的方法---“双重检查锁定”。
首先检测实例是否已经创建,如果没有创建,才进行同步。这样只进行一次同步,也是我们需要的、想做到的。
public class ChocalaterBoiled{ private boolean empty; //锅炉是否为空 private boolean boiled; //液体是否已经沸了 private volatile static self; //锁定实例 private ChocalaterBoiled(){} //私有构造方法 public static ChocalaterBoiled getInstance(){ //全局访问点 if(self==null){ Sychronized(ChocalaterBoiled.class){//双重检查 if(self==null) self=new ChocalaterBoiled(); } } return self; } public boolean isEmpty(){ return empty;} public boolean isBoiled(){ return boiled;} public void fill(){ //锅炉中添加未沸的液体 if(isEmpty() && !isBoiled()){ empty=false; } } public void drain(){ //液体沸了后,倒出液体 if(!isEmpty() && isBoiled()){ empty=true; boiled=false; } } public void boiled(){ if(!isEmpty() ){ boiled=true; } } }
这样,通过“双重检查锁定"方法,我们就很完美的实现了管理公共资源的单例设计模式的设计。
当然,还有一种更加好的单例模式:
public class SimpleSingleton{ private SimpleSingleton(){ } public static SimpleSingleton getInstance( ){ return SimpleSingletonHolder.INSTANCE } private static class SimpleSingletonHolder{ public static final INSTANCE = new SimpleSingleton(); } }
这种使用的是静态内部类的方式实现的,它避免了加锁,同时也保证了单例的定义。