首页 > 代码库 > 【养孩子这么贵,还是生一个算了吧】设计模式之单例模式Java版

【养孩子这么贵,还是生一个算了吧】设计模式之单例模式Java版

定义

单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。所以在需要保证让一个类只有一个实例时,可以参考采用单例模式。通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。唯一的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。


方法一

其一,不让其他类去实例化自己类的实例,那我们很容易想到可以通过定义一个private的构造函数。这是因为如果我们自定义了一个private的构造函数,那么系统默认的构造函数就会失效。当其他类使用new来实例化自身类对象时,就会发出无法访问自身类的构造函数的警告。这解决了防止实例化多个对象的问题。

其二,为了能够产生一个自身类的实例变量,我们可以定义一个该自身类对象的全局变量,并在自身类内部定义一个GetInstance函数获得该全局对象变量。因为在自身类内部是可以访问private的构造函数的。下面直接来看Java版的代码。

final class Singleton {
	private static Singleton instance;
	private Singleton() {};
	public static Singleton GetInstance() {
		if (null == instance) {
			instance = new Singleton();
		}
		return instance;
	}
}

public class MainClass {
	public static void main(String[] args) {
		Singleton s1 = Singleton.GetInstance();
		Singleton s2 = Singleton.GetInstance();
		
		if (s1 == s2) {
			System.out.println("两个实例相同");
		} else {
			System.out.println("两个实例不相同");
		}
	}
}


改进

试想,在上面的代码中,如果有两个线程同时进入GetInstance函数,结果会是怎么样呢?说到这里,你可能也已经明白了,上面的代码可能会存在多线程安全的问题。所以我们采用lock来防止不同进程同时进入创建自身类对象代码。见下面代码所示:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

final class Singleton {
	private static Singleton instance;
	private Singleton() {};
	public static Singleton GetInstance() {
		if (null == instance) {
			instance = new Singleton();
		}
		return instance;
	}
}

public class MainClass {
	private static Lock lock = new ReentrantLock();
	public static void main(String[] args) {
		
		Singleton s1 = Singleton.GetInstance();
		Singleton s2 = Singleton.GetInstance();
		lock.lock();
		try {
			if (s1 == s2) {
				System.out.println("两个实例相同");
			} else {
				System.out.println("两个实例不相同");
			}
		} finally {
			lock.unlock();
		}
	}
}

方法二

为了达到单例模式的目标,我们还可以使用java中一个叫做“静态初始化”的概念来完成。因为我们都知道一个静态变量是只是会初始化一次的。这个概念很简单,我们来看下面代码:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

final class Singleton {
	private static final Singleton instance = new Singleton();
	private Singleton() {};
	public static Singleton GetInstance() {
		return instance;
	}
}

public class MainClass {
	public static void main(String[] args) {
		
		Singleton s1 = Singleton.GetInstance();
		Singleton s2 = Singleton.GetInstance();
		if (s1 == s2) {
			System.out.println("两个实例相同");
		} else {
			System.out.println("两个实例不相同");
		}
	}
}


使用静态初始化的优点是线程安全的,不用进行上锁来保证。


方法一 VS 方法二

方法一,是类需要被实例化时才会被实例化;而方法二是类一旦被加载时就会被实例化,所以方法二需要提前占用系统资源。但是方法一在多线程安全上来看代码更为简洁,不需要额外的代码。





【养孩子这么贵,还是生一个算了吧】设计模式之单例模式Java版