java设计模式singleton原理及实现(java1.4前不要使用双重锁保证线程安全)
题外话:我要变强,要变强,变强,强。
1、 Singleton的应用场景以及为什么要使用single
Singleton是一生只能有一个实例的对象。只能由singleton自身创建一个实例。外人是无法创建实例的,外人只能使用。
由于单例模式一生只有一个对象,因此可以节省内存,加快访问速度。比较适合在公共场合使用。比如
1)对打印机的控制;如每台计算机可以连接多个打印机,只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机;
2)windows的task manager是典型的单例模式。
2、 Singleton的实现原理
1、 私有的构造方法
2、 私有静态引用指向自己实例
3、 公有静态方法返回自身实例
3、 Singleton的实现方式--懒汉模式(非线程安全)
package ss; /** * 非线程安全的懒汉模式 * * 优点: * 避免了在恶汉模式中没有调用却创建实例的情况,提高了资源利用率。 * 缺点: * 非线程安全,多个线程同时访问的时候可能会创建出多个实例,而且这多个实例不是同一个对象, * 虽然后面创建的实例会覆盖前面创建的实例,但是还是会出现拿到的实例不一样的情况。解决这 * 一个问题的办法就是加同步锁(synchronized)。 * */ public class Singleton02 { // 私有构造方法 private Singleton02(){ } // 供内部调用的实例 private static Singleton02 instance = null; // 外部访问的静态方法 public static Singleton02 getInstance(){ if(null == instance){ instance = new Singleton02(); } return instance; } }
4、Singleton的实现方式--懒汉模式(线程安全)
package ss; /** * 线程安全的懒汉模式(通过加synchronized) * 优点: * 避免了Singleton02中的可能创建多个实例的问题 * * 缺点 * 同步的开销比较大, * 事实上,仅仅在第一次调用的时候需要同步判断,其他时候只需要判断非空返回即可。 * */ public class Singleton03 { // 私有构造方法 private Singleton03(){ } // 供内部调用的实例 private static Singleton03 instance = null; // 外部访问的静态方法 public static synchronized Singleton03 getInstance(){ if(null == instance){ instance = new Singleton03(); } return instance; } }
5、Singleton的显示方式--饿汉模式(线程安全)
package ss; /** * 饿汉模式 * 优点: * 1、线程安全 * 2、在类的加载过程中已经创建好实例,加载速度快 * 缺点: * 1、资源效率不高,实例在某些情况下实例化后,可能永远不会调用到getInstance() */ public class Singleton01 { // 私有构造方法 private Singleton01(){} // 定义内部实例,只供内部调用 public static Singleton01 instance = new Singleton01(); // 公有外部调用方法 public static Singleton01 getInstance(){ return instance; } }
6、Singleton的实现方式--双重锁(java1.4以及1.4版本前不可靠)
package ss; /** * 双重锁模式 * 起源: * 为了应对线程安全Singleton03中的情况,双重锁应运而生。目的是为了避免对除了第一次调用外的所有调用都实行同步的昂贵代价。 * 同步的代价在不同的jvm间是不同的。在早期、代价相当高。随着更高级的jvm出现,同步的代价降低了,但synchronized方法或块依然有性能损失。 * 不考虑jvm的进步,程序员们绝对不想不必要的浪费处理时间。 * 优点: * Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。 * 而volatile使用时有明确的规定: * 对变量的写操作不依赖于当前值; * 该变量没有包含在具有其他变量的不变式中; * 只有在状态真正独立于程序内其他内容时才能使用 volatile。 * java内存的无序写入将会导致,双重锁定失败的问题。 * 值得注意的是双重检查锁不适用于1.4及更早的java版本。
* ********
* 引用自《Head First Design Pattern》
* 不幸的是,在1.4以及更早版本的Java中,许多JVM对volatile关键字的实现会导致双重检查加锁的失效。如果你不能使用Java 5,而必须使用旧版的Java, 就请不要利用此技巧实现单件模式。
* ******** * 如何解决可参照博客: * http://blog.csdn.net/chenchaofuck1/article/details/51702129 * */ public class Singleton04 { // 私有的构造函数 private Singleton04(){} // 内部访问的实例 private static volatile Singleton04 instance = null; // 外部访问的静态类 public static Singleton04 getInstance(){ if(null == instance){ synchronized(Singleton04.class){ if(null == instance){ instance = new Singleton04(); } } } return instance; } /* // 下面代码会出现这样的情况, // 当instance为null的时候,两个线程并发进入if语句内部。然后,一个线程进入synchronized块来初始化instance, // 而另一个线程则被阻断。当一个线程退出synchronized块时,等待着的线程进入并创建另一个singleton对象 // 基于此种情况,出现了“双重检查锁定” public static Singleton04 getInstance(){ if(null == instance){ synchronized(Singleton04.class){ instance = new Singleton04(); } } return instance; } */ }