Java-单例模式

作者 : 开心源码 本文共3271个字,预计阅读时间需要9分钟 发布时间: 2022-05-12 共180人阅读

单例模式是我们实际开发中常用到的开发模式,目的是保证明例的唯一性,确保这个类在内存中只会存在一个对象,但我们现在用到的单例模式相关代码可能不是最优的,今天让我们探究一下单例模式的正确写法。
单例模式通常分为饿汉式和懒汉式,我们这里来一个最简单的代码:
饿汉式相关代码:

public class SingletonPattern {//无参构造私有化,不允许直接new取得实例    private SingletonPattern() {    }//创立静态啊私有实例    private static SingletonPattern hungerSingleton =new SingletonPattern();//同过公共静态方法获取实例,确保唯一    public static SingletonPattern getHungerInstance(){        return hungerSingleton;    }    }

缺点:

提前创立类实例,无论能否需要,只需类加载就进行了实例化,白费资源.

懒汉式相关代码:

//懒汉式,    private static SingletonPattern lazySingleton;    public static SingletonPattern getLazyInstance(){//判断假如没有实例创立,则创立实例      if(lazySingleton == null){          lazySingleton =new SingletonPattern();      }        return lazySingleton;    }

缺点:

需要的时候创立类实例,没有考虑到多线程,多线程环境下无法保证单例效果,会屡次执行SingletonPattern instance=new SingletonPattern()

懒汉式和饿汉式主要区别能否先创立类的实例,一个是拿时间换空间,一个是拿空间换时间,懒汉式只有我需要他的时候才去加载它,懒加载机制,饿汉式不论需不需要我先在内存中开拓空间。这两种是最基本的单列模式,接下来我们就以懒汉式为例,分析如何正确创立和使用单例。

我们分析上面的懒汉式代码的问题是:多线程情况下无法保证单例,也就是说:假如同时多个线程访问可能会创立多个实例的情况出现.那我们首先想到的是-synchronized,来进行同步方法或者同步块,代码如下:

    //懒汉式加锁同步    private static SingletonPattern syncSingleton;    public static SingletonPattern getSyncyInstance(){        if(syncSingleton == null){            //在此处加锁同步比在方法出加锁同步缩小了范围,性能稍高            synchronized (SingletonPattern.class){                syncSingleton =new SingletonPattern();            }        }        return syncSingleton;    }

缺点:

尽管使用了synchronized进行了线程的同步,还是会存在屡次执行的可能SingletonPattern instance=new SingletonPattern()

进一步优化采用DCL(Double Check Lock)双重检查来确定单例模式的线程安全和唯一,相关代码如下:

//懒汉式双层检查    private static SingletonPattern dclSingleton;    public static SingletonPattern getDCLInstance(){        if(dclSingleton == null){//第一层检查            //在此处加锁同步比在方法出加锁同步缩小了范围,性能稍高            synchronized (SingletonPattern.class){                if(dclSingleton == null){//第二层检查                    dclSingleton =new SingletonPattern();                }            }        }//此处有可能多线返回null对象。导致崩溃        return dclSingleton;    }

缺点:
首先我们先理解一下:通常我们进行实例化包含以下几步:
1.给 Singleton 实例分配内存,将函数压栈,并且申明变量类型;
2.初始化构造函数以及里面的字段,在堆内存开拓空间;
3.将 instance 对象指向分配的内存空间;

java 编译器允许执行无序,并且 jdk1.5之前不限制解决器重排序,不能保证按序执行,解决器会进行指令重排序优化导致程序崩溃。举例:正常顺序是1-2-3,优化重排后执行顺序可能为:1-3-2, 这时如果有 A 和 B 两条线程, A线程执行到3的步骤,但是未执行2,这时候 B 线程来了抢了权限,判断不为空,直接取走 instance可能会造成程序崩溃。

也就是说在jdk1.5之前有两个问题:

  • 线程间共享变量不可见性;
  • 无序性(执行顺序无法保证);
    为理解决jdk1.5存在的上述问题,我们需要在一个线程进行初始化等操作的时候对其余进入的线程可见,执行完毕后,其余线程在进行操作,这就又引出一个关键字-volatile
    相关代码:
  //volatile+懒汉式双层检查(DCL,Double Check Lock)    private static volatile SingletonPattern volatileSingleton;    public static SingletonPattern getVolatileInstance(){        if(volatileSingleton == null){//第一层检查            //在此处加锁同步比在方法出加锁同步缩小了范围,性能稍高            synchronized (SingletonPattern.class){                if(volatileSingleton == null){//第二层检查                    volatileSingleton =new SingletonPattern();                }            }        }        return dclSingleton;    }

分析:
先说一下volatile关键字的两大作用:

  • 可以保证在多线程环境下,变量的修改可见性
  • 提供内存屏障,来保证某些指令顺序解决器不能够优化重排,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的解决器重排序。

所以使用volatile关键字可以保证明例化的赋值操作是最后一步完成,实现了正确的单例模式。

其余单例的实现方法:

  • 静态内部类
  • 枚举
    采用静态内部类也是一种不错的选择,理由是静态内部类在没有显示调用的时候是不会进行加载的,当执行了return 后才加载初始化。
    相关代码:
 public static SingletonPattern InnerSingletonInstance(){        return staticSingleInstance.staticSingleton;    }    private static class staticSingleInstance{        private static SingletonPattern staticSingleton=new SingletonPattern();    }

枚举类是java1.5才出现的,采用枚举的方式除了写起来很简便,还有个好处是安全:由于JVM会保证enum不能被反射并且构造器方法只执行一次,但枚举会很耗内存,所以看情况而定吧
相关代码如下:

    //枚举实现    public static SingletonPattern getEnumInstance(){        return EnumSingleton.INSTANCE.getEnumSingleton();    }    private  enum EnumSingleton {        INSTANCE;        private SingletonPattern enumSingleton;        //JVM会保证此方法绝对只调用一次        private SingletonPattern getEnumSingleton() {            enumSingleton = new SingletonPattern();            return enumSingleton;        }    }

关于单例模式的实现,今天就说这么多,其实核心就是:构造私有,并且通过静态方法获取一个实例,在这个过程中必需保证线程的安全性。
告辞。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Java-单例模式

发表回复