单例模式:运行期间有且仅有一个实例
1.一个类只有一个实例
2.必须自行创建这个实例
3.必须自行向整个系统提供这个实例
懒汉模式:
在类加载时不创建实例,运行调用时创建。类加载快,在运行时获取对象慢。
//懒汉模式
public class Pet {
private Pet(){
}
private static Pet pet=null;
public static Pet getInfo(){
if(pet==null){
pet=new Pet();
}
return pet;
}
}
饿汉模式:
在类加载时创建实例。类加载慢,在运行时获取对象快。
//饿汉模式
public class Pet {
private Pet(){
}
private static Pet pet=new Pet();
public static Pet getInfo(){
return pet;
}
}
饿汉模式线程安全,但是,懒汉模式线程安全性不高,若是多线程,又怎能保证单例?
第一种方法:同步
在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的
public static synchronized Pet getInfo(){
if(pet==null){
pet=new Pet();
}
return pet;
}
第二种方法:双重检查锁定
在getInfo中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗
public static Pet getInfo(){
if(pet==null){
synchronized(Pet.class){
if(pet==null){
pet=new Pet();
}
}
}
return pet;
}
第三种方法:静态内部类
利用了classloader的机制来保证初始化时只有一个线程,所以也是线程安全的,同时没有性能损耗
public class Pet {
private static class LazyHolder {
private static final Pet INSTANCE = new Pet();
}
private Pet (){}
public static final Singleton getInfo() {
return LazyHolder.INSTANCE;
}
}
资源加载和性能:
饿汉模式在类创建的同时就实例化一个静态对象,不管之后会不会使用这个单例,都会占用一定的内存,但是相应的,在第一次调用时速度也会非常快,因为其资源已经初始化完成。
懒汉模式会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉模式一样可。