单例模式
来源:互联网 发布:淘宝网新款毛衣 编辑:程序博客网 时间:2024/06/01 07:44
饿汉式
这就是所谓的饥饿模式,因为单例的实例被声明成final和static变量,在第一次加载类到内存中时就会初始化,所以创建实例本身是线程安全的。
但是这可能带来潜在的性能问题:如果这个对象占用空间很大,没有使用这个对象之前,就把它加载到了内存中去是一种巨大的浪费。
第二种问题:Singleton实例的创建是依赖参数或者配置文件的,在getInstance()之前必须调用某个方法设置参数给它,那么饿汉式就无法使用。
public class Singleton { //类加载时就初始化 private final static Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; }}针对这种情况,可以对饿汉式进行改进,使用一种新的设计思想——延迟加载 (Lazy-load Singleton)。
懒汉式,线程不安全
懒汉式代码简单明了,而且使用了延迟加载模式,但是却存在致命的问题。当有多个线程并行调用 getInstance() 的时候,就会创建多个实例。也就是说在多线程下不能正常工作,即线程不安全。public final class Singleton{ private static Singleton instance = null; private Singleton() {} public static Singleton getInstance() { if(null == instance) instance = new Singleton(); return instance; }}
懒汉式,线程安全
为了解决线程不安全的问题,最简单的方法是将整个 getInstance() 方法设为同步(synchronized)。public final class Singleton{ private static Singleton instance = null; private Singleton() {} public static synchronized Singleton getInstance() { if(null == instance) instance = new Singleton(); return instance; }}
虽然做到了多实例线程安全,但是并不高效,每个线程调用 getInstance()都要加锁,并且其他线程需要检验锁是否存在。因为在任何时候只能有一个线程调用 getInstance()方法。但是同步操作只需要在第一次调用时才被需要,即第一次创建单例实例对象时。这就引出了双重检验锁。
双重检验锁
双重检验锁模式(double checked locking pattern),是一种使用同步块加锁的方法。创建实例时会有两次检查 instance == null,一次是在同步块外,一次是在同步块内。为什么在同步块内还要再检验一次?因为可能会有多个线程一起进入同步块外的 if,如果在同步块内不进行二次检验的话就有可能生成多个实例。public final class Singleton{ // 线程不安全 private static instance = null; private Singleton() {} public static Singleton getInstance() { if(null == instance) { //Single Checked synchronized(Singleton.class) { if(null == instance) //Double Checked instance = new Singleton(); } } return instance; }}双重检验锁代码看起来很完美,很可惜,它是有问题。主要在于 instance = new Singleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面三件事情。
- 给 instance 分配内存;
- 调用 instance 的构造函数来初始化成员变量;
- 将 instance 对象指向分配的内存空间(执行完这步instance就为非 null)。
解决指令重排序优化问题,只需要将 instance 变量声明成 volatile。volatile 修饰的话就可以确保 instance = new Singleton();对应的指令不会重排序,保证线程安全。
public class Singleton { // 线程安全 private volatile static Singleton instance; //声明成 volatile private Singleton () {} public static Singleton getSingleton() { if (null == instance) { synchronized(Singleton.class) { if (null == instance) { instance = new Singleton(); } } } return instance; }}
静态内部类 static nested class
使用内部类来做到延迟加载对象,在初始化这个内部类的时候,由于 SingletonHolder 是私有的,除了 getInstance()之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。这种写法最大的美在于,完全使用了 Java 虚拟机的机制进行同步保证,没有一个同步的关键字。public class Singleton { private static class SingletonHolder { private final static Singleton INSTANCE = new Singleton(); } private Singleton () {} public final static Singleton getInstance() { return SingletonHolder.INSTANCE; }}
枚举
实现单例模式的最佳方法。它不仅能避免多线程同步问题;而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化;而且不能通过 reflection attack 来调用私有构造方法。
public enum Singleton { INSTANCE; public void whateverMethod() { }}枚举类本质上是一种多例模式,只有有限个对象可以创建,而且无法显式调用构造函数创建对象。
0 0
- 单例、单例模式
- 单例模式-多线程单例模式
- 单件模式(单例模式)
- 设计模式------单例模式
- 设计模式------单例模式
- 设计模式-单例模式
- 设计模式 - 单例模式
- 设计模式---单例模式
- 设计模式---单例模式
- PHP模式-单例模式
- 【设计模式】单例模式
- 设计模式-单例模式
- 设计模式----单例模式
- 设计模式--单例模式
- 设计模式-单例模式
- 单例模式(单子模式)
- 设计模式-单例模式
- [设计模式] 单例模式
- 看敏捷高手交互卓越软件
- 下载Spring源码的步骤和遇到的坑
- spring事物处理异常
- Transaction rolled back because it has been marked as rollback-only”
- Spring Security 3 证书登录
- 单例模式
- FFMPEG发布RTSP流
- Android系统源代码情景分析
- 蓝桥杯——大臣的旅费(树的最大直径)
- MemCached缓存
- 项目中的.NET
- linux下升级gcc的方法
- Eclipse导入Android项目 Eclipse常见错误 中文乱码问题
- Spring线程池和JDK线程池的区别及与FutureTask配合使用得到任务执行结果