单例模式:确保一个类只产生一个实例
好处:
(1)对于频繁使用的对象,可以省去创建对象的时间
(2)new操作的次数变少,系统内存使用率会降低,减轻GC的压力
一个简单的单例模式:
public class Singleton(){
private static Singleton instance = null;
private Singleton(){
System.out.println("Singleton is create");
}
public static Singleton getInstance(){
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
单例类必须要有一个private访问级别的构造函数,只有这样才能确保单例不会在系统的其他代码中被实例化。其次,instance和getIntance必须是static修饰。
上面简单的单例模式在多线程的情况下会出现问题:
//简书没有画图的功能吗。。。图待补充
直接在getInstance()上加synchronized,因为每次getInstance都要进行加锁操作,会有效率问题。所以考虑使用双检锁模式:
public class Singleton(){
private static Singleton instance = null;
private Singleton(){
System.out.println("Singleton is create");
}
public static Singleton getInstance(){
if(null == instance){
synchronized(Singleton.class){
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
}
两次检查,只有在第一次检查为空是会加锁,大大提高了效率。
这里还有一个问题,有些系统会对代码进行重排序,上面的写法极低的概率下也会发生问题,主要是instance = new Singleton();这一句。这行代码可以分解成三步:
1.分配内存空间
2.初始化对象
3.将初始化的对象指向分配的内存空间
JVM有可能对2、3两步进行重排列,这样重排列后的顺序就是:
1.分配内存空间
2.将对象指向分配的内存空间
3、对象初始化
多线程的情况下也会发生问题
//缺图。。。
优化后的双检锁模式
public class Singleton(){
private static volatile Singleton instance = null;
private Singleton(){
System.out.println("Singleton is create");
}
public static Singleton getInstance(){
if(null == instance){
synchronized(Singleton.class){
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
}
volatile关键字可以禁止代码重排,解决上述问题。