单例模式
2018-02-28 16:03
211 查看
单例模式定义
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式优缺点
优点
在内存中只有一个实例,节约内存,减少性能开销。当一个对象的产生需要占用较多资源时(如文件、读取配置文件等),采用单例模式可有效节约资源。单例对象可以作为全局访问点,优化和共享全局资源访问。
缺点
单例模式要求自行实例化,因此单例模式一般没有接口,拓展性不好。单例模式使用场景
要求生成唯一序列号的场景在整个项目中需要一个共享访问点或共享资源
创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源
需要定义大量的静态变量和静态方法的场景
单例模式代码形式
饿汉形式
public class Singleton{ private static Singleton instance = new Singleton();//在类加载时即进行创建 private Singleton(){} public static Singleton newInstance(){ return instance; } }
饿汉形式中该对象在类加载的时候就完成了对象的创建,不存在线程安全问题。当对象占用内存小而且初始化后很快就会被用到时,饿汉形式很适用;但是如果这个对象并不急着用或者根本没有用到,该对象还是会被创建,如果该对象占用的内存空间较大,则造成了系统资源的浪费。
懒汉形式
public class Singleton{ private static Singleton instance = null; private Singleton(){} public static Singleton newInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }
懒汉形式中该对象时在需要的时候才去创建,即按需使用,在单例使用次数少、占用资源多的场景下比较适用。但是懒汉形式并不是线程安全的,当有多个线程同时调用newInstance方法时,可能会创建多个实例。
线程安全的懒汉形式
public class Singleton{ private static Singleton instance = null; private Singleton(){} public static synchronized Singleton newInstance(){//使用synchronized关键字同步 if(instance == null){ instance = new Singleton(); } return instance; } }
这种形式下,线程每次调用newInstance方法都会先获取对象锁,然后再执行方法体,在高并发情况下,对象锁有可能升级为重量级锁,带来一定的性能问题。
一种看起来可行的改进方式–双重检查锁定形式
既然存在性能问题,我们将代码稍加改进:public class Singleton{ private static Singleton instance = null; private Singleton(){} public static Singleton newInstance(){ if(instance == null){ synchronized(Singleton.class){ if(instance == null){ instance = new Singleton(); } } } return instance; } }
在这种形式的代码下,只要对象被线程安全的创建成功,将不会在存在性能问题。然而既然是“看起来可行的”办法,那么一定有什么隐患。
可靠的改进方式–volatile
上面的双重检查锁定形式不可靠的地方在于它并不能保证线程安全,根本原因在于instance = new Singleton();这一行代码可以分为如下三行伪代码:
memory = allocate();//1.分配内存 initInstance(memory);//2.初始化 instance = memory;3.设置引用指向内存地址
我们知道在java代码的各级编译过程中都存在执行重排序的情况,如果上述代码中2和3被重排序,3先于2执行,并不违反单线程as-if-serial语义和多线程happens-before语义,再如果在2执行之前发生线程切换,另外一个线程在判断instance变量是否为Null时将返回false,并返回Instance,然而此时instance对应的内存区域并没有被初始化为Singleton对象。
那么我们应该如何处理这种情况呢?很简单,禁止
instance = new Singleton();这行代码指令重排序,为此我们给instance变量引入happens-before语义限制—-就是用
volatile关键字修饰instance变量。可靠的改进方式:
public class Singleton{ private static volatile Singleton instance = null; private Singleton(){} public static Singleton newInstance(){ if(instance == null){ synchronized(Singleton.class){ if(instance == null){ instance = new Singleton(); } } } return instance; } }
静态内部类形式
public class Singleton{ private static class SingletonHolder{ public static Singleton instance = new Singleton(); } public static Singleton newInstance(){ return SingletonHolder.instance; } }
在这种形式中,巧妙地利用的jvm的类加载机制,实现了线程安全的懒汉式实例化。
当线程调用newInstance方法时,会触发静态内部类SingletonHolder的加载和初始化,这里不存在线程安全问题;同时当没有线程调用newInstance方法时,静态内部类将不会被加载。
总结
由上面的分析可知,静态内部类形式和volatile关键字改进的双重检查锁定形式最能称得上是单例模式的最佳实践。相关文章推荐
- 设计模式(20)--观察者模式
- Java设计模式之《适配器模式》及应用场景
- linux 在 rescue 模式下修复 MBR(转)
- 游戏开发中常用的设计模式
- Google Maps带来的新型WebGIS设计模式
- Observer Pattern(观察者模式)及其在C#中的实现
- 单例模式的几种写法
- 策略模式场景举例
- 企业微信开发者回调模式
- 如何取消CentOS 的图形界面直接进入命令行模式
- GoF之Singleton 单例模式
- 设计模式:模板模式
- Spark资源调度分配内幕解密:Driver在Cluster模式下的启动、两种不同的资源调度方式源码彻底解析、资源调度内幕总结
- 《JAVA与模式》之装饰模式
- 观察者设计模式( Observable类Observer接口)
- JAVA微信扫码支付模式一功能实现
- 2013-7-10-本周设计模式: Copy Document
- 设计模式读书笔记-----备忘录模式
- (12)策略模式
- 最常用的设计模式