什么是单例模式?
我们只允许某个类存在唯一的一个实例 - 换句话说,对于某个类我们只new 一次
为什么使用单例?
比如线程池, Metrics Object, 或者Logging Object etc. 单例可以用来减少对资源的损耗,因为对于相同目的操作我们应该尽可能的重用而不是重建。
如何使用单例?
方法一
package Chap5SingletonPattern; public class Singleton_1 { private static Singleton_1 instance; private Singleton_1() {} public static Singleton_1 getSingleton() { if (instance == null) { instance = new Singleton_1(); } return instance; } }
这是最简单的方法,但是他是线程不安全的。 当多个线程同时访问 getSingleton 这个方法的时候,Singleton_1 可能会创建多个对象,这就打破了singleton pattern 的原则。
方法二
package Chap5SingletonPattern; public class Singleton_2 { private static Singleton_2 instance; private Singleton_2() {} public static synchronized Singleton_2 getSingleton() { if (instance == null) { instance = new Singleton_2(); } return instance; } }
通过添加 synchronized 关键字,我们把getSingleton 这个方法给转换成 thread safe 模式,这解决了线程安全问题,但是 synchronization 是比较消耗资源的,不仅如此,我们这里的线程安全指的是 当多个线程同时并且第一次 访问 getSingleton ,当第二次访问的时候就不存在问题。所以有点 overkill - 性能比较见。
方法三
package Chap5SingletonPattern; public class Singleton_3 { private static Singleton_3 instance = new Singleton_3(); private Singleton_3() {} public static Singleton_3 getSingleton() { return instance; } }
我们依赖JVM 来创建 singleton object, 我们知道static variable 是在 JVM load class 的时候才会创建的所以 这里保证了 线程的安全. 他的性能和方法一的基本相同.
值得注意的是,JVM load class 只是load 要被使用的class。 所以这种方法能造成一种情况就是 我们 创建了这个singleton object 但是没有任何一个 thread来的及使用它。这就造成了某种程度上的浪费。
方法四
package Chap5SingletonPattern; public class Singleton_4 { private volatile static Singleton_4 instance; private Singleton_4() {} public static Singleton_4 getSingleton() { if (instance == null) { synchronized(Singleton_4.class) { instance = new Singleton_4(); } } return instance; } }这里我们同时使用了 volatile 和 synchronized 关键字 - Double-Chekced locking. 保证了我们就在第一次的时候进行锁操作,并且保证了 当我们 在 某个进程的cache上实例化 singleton instance的时候,这个instance被扩散到所有的线程里面。
Synchronized 关键字 的性能
通过运行以下代码发现,Singleton_1 (无syncronized 关键字)的运行时间大概在14ms;但是SIngleton_2(有关键字)的运行时间大概在38ms。差距hin大啊.
package Chap5SingletonPattern; public class main { public static void main(String[] args) { long startTime = System.currentTimeMillis(); int threadNo = 1000000; main m = new main(); for (int i = 0; i < threadNo; i++) { runtest r = m.new runtest(); r.run(); } long stopTime = System.currentTimeMillis(); System.out.println(stopTime - startTime); } public class runtest implements Runnable { public runtest() { } public void run() { Singleton_1.getSingleton(); //Singleton_1 无synchronized 关键字,Singleton_2 有。 } } }
有问题欢迎指出!