- 定义:确保一个类只有一个实例,并提供全局访问点
单例模式应该是设计模式中最容易理解的一个模式了,但是要写一个健壮的单例类,还是要花点功夫的。
来看一个经典的单例模式实现:
public class Singleton{
private static Singleton uniqueInstance;
private Singleton(){}
public static Singleton getInstance(){
if(uniqueInstance==null)
uniqueInstance = new Singleton();
return uniqueInstance;
}
//other methods
}
这是一个经典的单例模式实现,但是有点问题,它只适合在单线程中使用,如果在多线程环境中,它就懵逼了,因为可能同时有几个线程同时判断对象为空,然后创建对象,所以在多线程中要使用单例模式,就需要进行改进优化。
备选方案一:将方法修改成同步方法,行不行?
答案是行也不行,意思就是虽然能解决问题,但是却带来了更大的问题,因为只有在第一次调用的时候才有必要同步,当实例化之后就没有同步的必要了,每次的同步调用会带来严重的性能问题,所以这种方法不可取。
备选方案二:“饿汉”单例,就是说在静态初始化时直接创建对象,这就保证了每次获取的都是一开始初始化时候创建的那个对象,保证了线程安全。
示例:
public class Singleton{
private static Singleton uniqueInstance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){return uniqueInstance;}
}
这个方案有效的解决了之前的问题,但是还是有缺点就是浪费空间资源。
备选方案三:“双重检查加锁”方式,多说无益,直接上代码
public class Singleton{
private volatile static Singleton uniqueInstance ;
private Singleton(){}
public static Singleton getInstance(){
if(uniqueInstance==null){
synchronized(Singleton.class){
if(uniqueInstance==null)
uniqueInstance = new Singleton();
}
}
return uniqueInstance;
}
}
volatile是一个关键字,作用就是保证变量的可视性,在多线程对于共享变量的操作,主要关注两个方面:原子性,可视性。volatile修饰的变量可以在修改之后及时被其他线程读取到。由于虚拟机对volatile的实现问题,这个方案的必须在Java5以上的版本才有效,估计这会儿也没人会用那么低版本的Java了。
以上这些就是单例模式中一些常用实现方式,在实际应用中也是比较常见的。
参考书籍:《Head First设计模式》 作者:Eric Freeman&Elsabeth Freeman&Kathy Sierra&Bert Bates
菜鸟手书,欢迎指正!