定义
单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。所以在需要保证让一个类只有一个实例时,可以参考采用单例模式。通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。唯一的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
方法一
其一,不让其他类去实例化自己类的实例,那我们很容易想到可以通过定义一个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 方法二
方法一,是类需要被实例化时才会被实例化;而方法二是类一旦被加载时就会被实例化,所以方法二需要提前占用系统资源。但是方法一在多线程安全上来看代码更为简洁,不需要额外的代码。