阅读Head First 设计模式之单例模式

    单例模式,顾名思义,就是一个类只能创建一个实例并提供一个全局访问点

    该怎么实现单例模式那?

    第一步:在JAVA面向对象的语言中,要想创建一个实例对象,只需new Object()即可,但正常情况下我们可以新建许多个实例对象。实例化一个对象时其实是实例化其构造方法,构造方法默认作用域是public的,这个导致该对象可以被外部实例化而没有限制,若该类构造方法的作用域改成private,我们只能在该类内部来新建实例对象。

    第二步:既然第一步确保了新建实例对象是自己可控后,怎么确保只能创建一个实例对象?由于外部对象不能实例化该对象,我们需要提供一个静态的获取实例的方法供外部调用,可以在该方法中控制只能实例化一次。

    一.懒汉式单例模式

      1.1  只有在调用类的实例化方法时,才创建实例对象,线程不安全。

public class Singleton {

	// 利用一个静态变量来记录Singleton的唯一实例
	private static Singleton uniqueInstance;

	// 把构造器声明为私有,只有来自Singleton类内才可以调用构造器
	private Singleton() {
	}

	// 提供公共的静态实例方法供外部调用,并返回Singleton类的唯一实例
	public static Singleton getInstance() {
		if (uniqueInstance == null) {
			uniqueInstance = new Singleton();
		}
		return uniqueInstance;
	}

}

 通过多线程环境测试是否只产生一个实例:

	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			new Thread(new Runnable() {

				@Override
				public void run() {
					System.out.println(Singleton.getInstance());
				}
			}).start();
		}

	}

结果显示并不能保证线程安全,不一定只产生一个实例:

1.2 为了保证线程安全,在牺牲性能的情况下,通过getInstance方法加同步

public class Singleton {

	// 利用一个静态变量来记录Singleton的唯一实例
	private static Singleton uniqueInstance;

	// 把构造器声明为私有,只有来自Singleton类内才可以调用构造器
	private Singleton() {
	}

        // 提供公共的静态实例方法供外部调用,并返回Singleton类的唯一实例
	// 为了保证线程安全,加了同步代码块
	public synchronized static Singleton getInstance2() {
		if (uniqueInstance == null) {
			uniqueInstance = new Singleton();
		}
		return uniqueInstance;
	}

}

结果显示能保证线程安全,只产生一个实例:

    1.3 以上方法虽然保证只产生一个实例,但性能差,于是改成同步代码块并加双重锁判断

public class Singleton {

	// 利用一个静态变量来记录Singleton的唯一实例
	private static Singleton uniqueInstance;

	// 把构造器声明为私有,只有来自Singleton类内才可以调用构造器
	private Singleton() {
	}

	// 提供公共的静态实例方法供外部调用,并返回Singleton类的唯一实例
	// 同步代码块并加双重锁判断,即保证线程安全,性能也不会差
	public static Singleton getInstance3() {
		if (uniqueInstance == null) {
			synchronized (Singleton.class) 
			{
				if(uniqueInstance == null) {
					uniqueInstance = new Singleton();
				}
			}
		}
		return uniqueInstance;
	}

}

二.饿汗式单例模式

在类加载时,就获取到类的实例,能保证线程安全,但不管是否需要,都会获取类的实例,影响性能。

public class Singleton {

	// 利用一个静态变量来记录Singleton的唯一实例,加载类时就获取
	private static Singleton uniqueInstance = new Singleton();

	// 把构造器声明为私有,只有来自Singleton类内才可以调用构造器
	private Singleton() {
	}

	// 提供公共的静态实例方法供外部调用,并返回Singleton类的唯一实例
	// 恶汉式
	public static Singleton getInstance4() {
		return uniqueInstance;
	}

}

三.静态内部类单例模式

                首先利用jvm的加载机制保证只能产生一个实例,其次静态内部类也是一个单独的class文件,只有在使用时才加载,保证了效率。书中并没提及该方法,通过网上查询资料找的。

public class Singleton {

	// 利用静态内部类来记录Singleton的唯一实例
	private static class SingletonHolder {
		private static Singleton uniqueInstance = new Singleton();
	}

	// 把构造器声明为私有,只有来自Singleton类内才可以调用构造器
	private Singleton() {
	}
	
	// 提供公共的静态实例方法供外部调用,并返回Singleton类的唯一实例
	public static Singleton getInstance5() {
		return SingletonHolder.uniqueInstance;
	}

}

总结: 使用单例模式,为了兼顾线程安全与性能,推荐使用同步代码块并加双重锁判断的懒汉式或静态内部类的形式。

参考:https://blog.csdn.net/twocold_2010/article/details/53241056

猜你喜欢

转载自blog.csdn.net/sunjian1122/article/details/79332734