单例模式: 保证一个类只有一个实例, 并提供一个全局访问点。(可以说是最简单的模式, 从零单排冲天梯)
类图:
抽象实现:
1. 懒汉模式 (懒得判断是否已经实例化, 先进行实例化)
package com.wenniuwuren.singleton; public class Singleton { // 懒汉模式 /** * 私有且唯一的属性 */ private static Singleton uniqueInstance = new Singleton(); /** * 私有构造器保证不能被继承 */ private Singleton() { } /** * 全局访问点 static的修饰至关重要--提供给外部在不能实例化内部代码的时候通过类名访问内部public方法 */ public static Singleton getInstance() { return uniqueInstance; } }适用于对程序性能有较大要求的地方, 如果用饱汉模式(详情见下文)要对线程加同步锁降低系统性能。
2. 饱汉模式(采用了volatile和同步块来保证并发安全性, 不像上面的懒汉模式直接就实例化了, 因为饱汉模式存在着判断和赋值,所以必须加上安全机制保障并发的正确性。 例如:线程A判断uniqueInstance为null进入方法, 然后new了实例, 但是还没赋值给uniqueInstance, 这时CPU把时间给了线程B, 此时的uniqueInstance还未赋值所以又new了一次实例, 那么这就破坏了单例模式了)
package com.wenniuwuren.singleton; public class Singleton { // 饱汉模式 /** * 私有且唯一的属性 * volatile修饰符保证变量仅存在于内存, 每个线程仅在内存中共享该变量, 从而保证了并发的安全性 */ private volatile static Singleton uniqueInstance = null; /** * 私有构造器保证不能被继承 */ private Singleton() { } /** * 全局访问点 */ public static Singleton getInstance() { if (uniqueInstance == null) { // 为空时才进行创建实例(延迟创建实例有助于节省不必要的资源占用) synchronized (Singleton.class) { if (uniqueInstance == null) { return uniqueInstance = new Singleton(); } } } return uniqueInstance; } }
当然如果系统对性能要求不高, 饱汉模式可以不像上面那么复杂, 直接在getInstance()方法上加同步修饰字段synchronized即可。
但是上面的方法虽然已经把同步代码块缩减到最小, 但是还是对性能有一点影响, 接下来介绍另一种保障线程安全的方法并且不使用同步代码块和volatile字段修饰。
public class Singleton { private Singleton(){} /** * 类级的内部类,只有被调用到时才会实例化 */ private static class SingletonHolder{ private static Singleton instance = new Singleton(); } public static Singleton getInstance(){ return SingletonHolder.instance; } }
应用场景:
- 网站的计数器, 不然并发计数访客什么的数量肯定会乱
- 应用程序的日志, 并发写日志不控制安全性日志重复写入多次
- Spring中的Bean默认也是单例的
参考书籍:
《设计模式:可复用面向对象软件的基础》
《Java并发编程实战》