大话设计模式——单例模式(Singleton)

单例模式(Singleton)是java中一个比较常见的设计模式,单例对象主要是为了保证在一个JVM中,该对象只有一个实例存在。主要优势:

(1) 减少一些大型对象的开销,减少内存的占用

(2) 省去了new操作,较少了GC的压力

(3) 保证了一些核心类只被创建一次,比如一些核心的调度类,一些主要的操作类只被创建一次。

单例模式最常见的两种实现模式:饿汉式,懒汉式,内部静态类实现,双重检验锁模式

1.饿汉式:在进行类加载时就创建好 (添加了synchronized保证了线程的安全性)

package designPatterns;

public class SingletonHungry {
	
	private static SingletonHungry instance = new SingletonHungry();
	
	//私有构造函数,防止被实例化
	private SingletonHungry(){
		
	}
	
	public static SingletonHungry getInstance(){ 
		return instance;
	}

}   

 

2. 懒汉式:延迟加载,在用到的时候才进行创建

package designPatterns;

public class SingletonLazy {
    
    private static SingletonLazy instance = null;
    
    //私有构造函数,防止被实例化
    private SingletonLazy(){
        
    }

    public static SingletonLazy getInstance(){
        if(instance == null){
            instance = new SingletonLazy();
        }
        return instance;
    }
}

当然懒汉模式是线程不安全的,为了保证线程安全,我们可以将函数定义成synchronized的,但是实际上,并不是每次都需要同步的,只是在第一次创建对象的时候需要确保同步,因此我们可以只同步一个对象。如下

package designPatterns;

public class SingletonLazy {
	
	private static SingletonLazy instance = null;
	
	//私有构造函数,防止被实例化
	private SingletonLazy(){
		
	}

	/*
	public static SingletonLazy getInstance(){
		if(instance == null){
			instance = new SingletonLazy();
		}
		return instance;
	}
	*/
	
	public static SingletonLazy getInstance(){
		if(instance == null){
			synchronized(instance){
				if(instance == null){
					instance = new SingletonLazy();
				}
			}
		}
		return instance;
	}
}

  但是,由于java中创建对象和赋值操作是分开进行的,也就是说instance = new SingletonLazy()这句话是分步执行的。JVM的即时编译存在指令重派的优化,2,3步的操作是无法确定的。

       1. 给instance分配内存

       2. 调用构造函数初始化成员变量

       3. 将instance对象指向分配的内存空间(执行完这句话之后instance才会变成非null的)

 

两步执行的,可能先执行分配空间,再初始化,这也可能会导致A,B两个线程不同步,所以还有一种方法是使用内部静态类来进行实现,内部静态类在另一篇博客中有详细介绍,这里不再赘述。

3. 静态内部类,代码如下:

package designPatterns;

public class Singleton {
    
    //私有构造方法,防止被实例化
    private Singleton(){
        
    }
    
    //静态内部类创建实例
    private static class SingletonFactory{
        private static Singleton instance = new Singleton();
    }
    
    //获取实例
    public static Singleton getInstance(){
        return SingletonFactory.instance;
    }
    
    //如果该对象被用于序列化,可以保证对象在序列化前后保持一致
    public Object readResolve(){
        return getInstance();
    }

}

使用内部静态类的方法,只有在外部类被调用时才会被加载,然后new一个实例,这样就避免了锁的应用,一般建议使用这种方法,但还是需要根据实际需要来进行选择的。

 

4. 双重检验锁模式(虽然内部类的方法感觉更加好用一些,但是很多面试官想让你写的还是双检锁模式)

public class Singleton {
    private volatile static Singleton instance; //volatile的可见性
    private Singleton() {}
    public static Singleton getSingleton() {
        if(instance == null) {         //Single Check
            synchronized(Singleton.class) {
                if(instance == null) {      //Double Check
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

双检锁模式其实就是为了保证一个顺序。双检锁的出现保证了上述2,3句的执行顺序。

 

posted @ 2017-09-12 09:41  东木刀纹  阅读(188)  评论(0编辑  收藏  举报