在实际应用中,我们往往希望在使用的时候才进行类的加载,而不希望类初始化的时候就进行加载,所以单例模式又有了另外一种实现,懒汉模式
一.延迟加载代码如下
public class MyObject {
// 懒汉模式,在类初始化的时候不加载,当调用getInstance方法的时候才对MyObject进行加载
private static MyObject myObject;
// 私有构造方法,单例模式必须
private MyObject() {
}
public static MyObject getInstance() {
if(myObject == null) {
myObject = new MyObject();
}
return myObject;
}
}
延迟加载,就是如果MyObject.getInstance()不被调用的话,JVM里面永远不会有MyObject实例。
如果MyObject.getInstance()被调用的话,程序会先判断myObject是否为null,若为null,则调用私有构造方法创建一个MyObject实例。很明显,在多线程的情况下,会出现多个线程同时进行myObject==null的判断,进而创建多个实例,这就违反了单例模式的原则,后续会进行优化
优点:延迟加载
缺点:线程不安全
二.单例模式优化
针对上述情况中,在多线程环境下产生多个实例的问题,最直接的改善形式就是给方法加锁,这样当多个线程同时访问该方法时,就会进行排队
代码如下
public class MyObject {
// 懒汉模式,在类初始化的时候不加载,当调用getInstance方法的时候才对MyObject进行加载
private static MyObject myObject;
// 私有构造方法,单例模式必须
private MyObject() {
}
public static synchronized MyObject getInstance() {
if(myObject == null) {
myObject = new MyObject();
}
return myObject;
}
}
在getInstance()方法前加synchronized关键字,对getInstance()方法进行加锁,保证一次只能有一个线程对getInstance()方法进行访问,但此时,同样存在问题,什么问题,效率的问题,如果存在多次调用getInstance()的情况,会由于排队,产生很大的效率影响。
优点:延迟加载,线程安全
缺点:效率低下
三.单例模式再次优化
我们希望的是在进行初始化的时候,只加载一个实例,即对改方法进行加锁,但是我们后续调用的时候,不需要对该方法进行加锁,就有了如下的优化
public class MyObject {
// 懒汉模式,在类初始化的时候不加载,当调用getInstance方法的时候才对MyObject进行加载
private static MyObject myObject;
// 私有构造方法,单例模式必须
private MyObject() {
}
public static MyObject getInstance() {
if(myObject == null) {
synchronized (MyObject.class) {
if(myObject == null) {
myObject = new MyObject();
}
}
}
return myObject;
}
}
为何会在synchronized中再加一个null的判断,如果不加第二个null判断,在多线程的情况下,多个线程已经进入第一层null的判断,然后排队获得锁,当第一个线程获取锁后,进行MyObject的加载,然后释放锁,其他线程此时可以直接获得该对象,但由于没有第二层null判断,此时还是会进行MyObject的加载。
此时的懒汉模式既实现了延迟加载,又提高了访问效率,且是线程安全的。