优点:1.减少内存开支
2.减少性能开销
3.避免对一个西苑的多重占用
4.便于全局访问
缺点:
1.没有接口扩展困难
2.单例持有Context容易造成内存泄露,所以推荐使用Application的Context。
饿汉式(不管吃不吃得下,先把碗装满)
public class HungrySingleton{
private static final HungrySington intance = new HungrySingleton();
private HungrySingleton(){}
public static HungrySington getIntance(){
return intance;
}
}
1.线程安全。利用了类的加载机制,加载初始化静态变量,且被只会执行一次,且JVM会利用锁来同步多个线程对同一个类的初始化。这样就保证了构造方法只会调用一次。
2.不能懒加载。无论是否使用都会去初始化实例。
懒汉式(需要的时候再去获取)
public class LazySingleton{
private static final LazySingleton intance = null;
private LazySingleton(){}
public static LazySingleton getIntance(){
if(intance == null){
intance = new LazySingleton();
}
return intance;
}
}
优点是可以实现延迟加载。在类初次加载的时候,由于只是声明了这个静态的对象,但不会自动初始化 instance对象,所以称为懒汉式
缺点是线程不安全。多线程并发无法保证唯一的实例改进策略:需要保证线程安全
双重检查锁(在需要创建对象的时候再进行同步锁操作,即如果实例对象已经创建,那么多线程的状态下可以同时获取到实例对象)
public class DoubleCheckSingleton{
private volatile static DoubleCheckSingleton intance;
private DoubleCheckSingleton(){}
public static DoubleCheckSingleton getInstance(){
if(instance == null){
synchronized(DoubleCheckSingleton .class){
if(instance == null){
intance = new DoubleCheckSingleton();
}
return intance;
}
}
}
}
- 这种策略看似解决了每次都需要同步的问题,但是由于 instance = new DclSingleton(); 这个初始化是非原子性的操作(1,给实例分配内存,2,调用构造函数初始化成员字段,3,实例对象指向分配的内存空间 23不能保证顺序执行)。就是说这个在JVM中可能会分成几步执行,那么就会存在指令重排序的问题,所以需要继续改进:添加 volatile 关键字。
- 改进后,懒加载
- 线程安全
当声明对象的引用为volatile后,初始化中的重排序,在多线程环境中将会被禁止,从而在根本上解决了问题。但是volatile在jdk1.5以后才能正确工作。
原因看[volatile关键字的使用]
静态内部类
public class StaticInnerSingleton{
public static StaticInnerSingleton getIntance(){
return SingletonHolder.instance;
}
private StaticInnerSingleton(){}
private static class SingletonHolder{
private static final StaticInnerSingleton instance = new StaticInnerSingleton ();
}
}
当第一次加载Singleton类时不会初始化instance,只有在调用了getSingleton方法的时候才会触发instance被初始化。因此,第一次调用get'Singleton方法会导致虚拟机加载SingletonManager类,这种方式不仅能确保线程安全,也能保证单例对象的唯一性,同时也延迟了单例的实例化。所以单例模式推荐使用这种方式实现)
网友评论