当前位置: 首页 > 后端技术 > Python

深入理解单例模式的几种实现方法

时间:2023-03-25 19:23:45 Python

饿了么中文单例实现是指在类加载的时候就已经创建并初始化了,所以实例的创建过程是一个线程安全的公共类IdGenerator{privateAtomicLongid=newAtomicLong(0);privatestaticfinalIdGenerator实例=newIdGenerator();privateIdGenerator(){}publicstaticIdGeneratorgetInstance(){返回实例;}publiclonggetId(){returnid.incrementAndGet();但是饿了么中国式有一个缺点,它不支持延迟加载,也就是说在使用之前已经创建了实例,如果占用太多资源却不使用,会造成一定的问题它比较浪费,还是要根据具体情况来决定是否使用这种方法。不过也有人说,如果时间长了,那么用的时候再加载会影响性能,而且饿的人会吃力。AtomicLong是一种原子变量类型,提供线程安全的原子操作,可以保证在多线程环境下,获取id时不会出现重复id。代码中的构造函数用private修饰符修饰,保证外部代码无法通过构造函数初始化IdGenerator类。以上是一个简单的唯一增量身份证号生成器,采用Hungry式单例实现方式。实例实例被定义为静态常量。我们知道,在运行一个类时,首先要将它加载到JVM中。该类的加载过程分为三个阶段:加载、链接和初始化。链接阶段分为三个步骤:验证、准备和分析。在准备步骤中,将创建类或接口的静态状态。变量,并初始化静态变量的初始值。懒惰风格我们上面提到饿了么风格是不支持延迟加载的,所以懒惰风格是支持延迟加载的。惰性风格的实现是在获取实例的方法上加锁。公共类IdGenerator{privateAtomicLongid=newAtomicLong(0);私有静态IdGenerator实例;privateIdGenerator(){}publicstaticsynchronizedIdGeneratorgetInstance(){if(instance==null){instance=newIdGenerator();}返回实例;}publiclonggetId(){returnid.incrementAndGet();}}锁定的结果是性能下降。如果经常使用这个单例,性能问题会比较严重,需要考虑另一种方式来实现。doubledetection和doubledetection的singleton实现弥补了上面hungry和lazy风格的不足:无法延迟加载,性能低下的问题。具体实现方法是在获取实例时判断实例是否已经创建。如果是,则直接返回。这是第一次检测。如果不是,则进入同步块。进入同步块后,判断实例是否已经存在。如果存在,直接返回。这是第二次检测。如果不存在,它会同步创建一个实例。公共类IdGenerator{privateAtomicLongid=newAtomicLong(0);私有静态IdGenerator实例;privateIdGenerator(){//初始化代码}publicstaticIdGeneratorgetInstance(){if(instance==null){synchronized(IdGenerator.class){//表示synchronized保护当前类对象if(instance==null){实例=新的IdGenerator();}}}返回实例;}publiclonggetId(){返回id.incrementAndGet();}}惰性实现方式每次获取实例都需要输入同步代码,会造成多次获取和释放锁,造成性能损失,但双重检测其实只需要同步一次创建实例,然后获取实例中不需要输入synchronized块代码,大大提高了性能。静态内部类静态内部类的实现比双重检测更简单,既保证了性能又实现了延迟加载。公共类IdGenerator{privateAtomicLongid=newAtomicLong(0);privateIdGenerator(){//初始化代码}privatestaticclassSingletonHolder{privatestaticfinalIdGeneratorinstance=newIdGenerator();}publicstaticIdGeneratorgetInstance(){returnSingletonHolder.instance;}publiclonggetId(){返回id.incrementAndGet();}}SingletonHolder是一个静态内部类。使用privat可以使内部类对外界完全隐藏。加载外部类IdGenerator时,不会创建实例。只有在调用getInstance方法时才会加载SingletonHolder并创建一个实例。这样既有饿了么中国式的安全特性,也有懒加载的特性。enumeration枚举的实现是利用java枚举类型本身的特性来保证实例创建的线程安全和实例的唯一性publicenumIdGenerator{INSTANCE;私有AtomicLongid=newAtomicLong(0);publiclonggetId(){返回id.incrementAndGet();}publicstaticvoidmain(String[]args){IdGeneratorEnum.INSTANCE.getId();}}可以看出枚举的实现是最简洁的,jvm保证了线程安全和单实例。也可以有效防止序列化和反序列化造成多实例和使用反射创建多实例。枚举类型编译成class文件后,再进行反编译。publicfinalclassIdGeneratorextendsjava.lang.Enum{publicstaticfinalIdGeneratorINSTANCE;公共静态IdGenerator[]值();publicstaticIdGeneratorvalueOf(java.lang.String);公共长getId();publicstaticvoidmain(java.lang.String[]);static{};}反编译后的枚举类其实是继承了Enum类,而INSTANCE是一个静态常量,看似又回到了饿汉式,但比它多了懒加载和简洁的特点。欢迎来到我的博客,更多实测内容!!

最新推荐
猜你喜欢