首页 > 代码库 > 【养孩子这么贵,还是生一个算了吧】设计模式之单例模式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版