精华内容
下载资源
问答
  • 静态内部类实现单例
    千次阅读
    2022-01-05 09:39:42

    饿汉式单例类不能实现延迟加载,不管将来用不用始终占据内存,懒汉式单例类线程安全控制烦琐,而且性能受影响

    静态内部类实现单例模式就可以克服以上两种单例模式的缺点,如下所示👇

    
    public class Singleton {
    
        private static class HolderClass{
            private static final Singleton instance = new Singleton();
        }
    
        public static Singleton getInstance(){
            return HolderClass.instance;
        }
    }


    由于静态单例对象没有作为Singleton的成员变量直接实例化,因此类加载时不会实例Singleton,第一次调用getInstance()时将加载内部类HolderClass,在该内部类中定义了一个static类型的变量instance,此时会首先初始化这个成员变量,由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。由于getInstance()方法没有任何线程锁定,因此其性能不会造成任何影响。通过使用IoDH(Initialization Demand Holder),我们既可以实现延迟加载,又可以保证线程安全,不影响系统性能,不失为一种最好的Java语言单例模式实现方式.

    <<Java设计模式(刘伟)>>

    更多相关内容
  • 在它的核心结构中只包含一个被称为单例的特殊。通过单例模式可以保证系统中,使用单例模式的只有一个对象实例。单例应用场景Windows系统的任务管理器。Windows系统的回收站。操作系统的文件系统,一个操作系统...

    465d9d60764b7a4732491d9c6245a541.png

    概述

    单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,使用单例模式的类只有一个对象实例。

    单例应用场景

    1. Windows系统的任务管理器。
    2. Windows系统的回收站。
    3. 操作系统的文件系统,一个操作系统只能有一个文件系统。
    4. 数据库连接池的设计与实现。
    5. 多线程的线程池设计与实现。
    6. Spring中创建的Bean实例默认都是单例。
    7. Java-Web中,一个Servlet类只有一个实例。
    8. 等等...

    单例的实现要点

    单例模式要求类能够有返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法)。

    单例的实现主要是通过以下三个步骤:

    1. 将类的构造方法定义为私有方法。这样其他类的代码就无法通过调用该类的构造方法来实例化该类的对象,只能通过该类提供的静态方法来得到该类的唯一实例。
    2. 定义一个私有的类的静态实例。
    3. 提供一个公有的获取实例的静态方法。

    单例追求的目标

    1. 线程安全。
    2. 懒加载。
    3. 调用效率高。

    单例模式的常用写法

    常用的单例有以下5种写法,如果还有其他的写法,也基本是从以下5种稍微修改而来,由于内容基本一致,并且可能不是很常用,因此在本文中不再赘述。

    1.饿汉模式

    a7b782e32809630cd9b7e90ea05afa56.png

    饿汉模式,比较常见的一种写法。在类加载的时候就对实例进行初始化,没有线程安全问题;获取实例的静态方法没有使用同步,调用效率高;但是没有使用懒加载,如果该实例从始至终都没被使用过,则会造成内存浪费。

    总结:线程安全、非懒加载、效率高。

    是否推荐:可以使用,但不推荐。

    2.懒汉模式

    e1e678e291aa9334796ca521f5eb8758.png

    线程安全的懒汉模式,比较常见的一种写法。在第一次使用的时候才进行初始化,达到了懒加载的效果;由于获取实例的静态方法用synchronized修饰,所以也没有线程安全的问题;但是,这种写法每次获取实例都要进行同步(加锁),因此效率较低,并且可能很多同步都是没必要的。

    总结:线程安全、懒加载、效率低。

    是否推荐:可以使用,但不推荐。

    注:该模式还有另一种常见写法,就是把getInstance方法上的synchronized去掉,这种方法有线程安全问题,不能使用。

    3.双重检测机制(DCL)

    9862c46e9070ca6ce2c34954786d8faf.png

    双重检测机制(双重检查加锁),比较常见的一种写法。在第一次使用的时候才进行初始化,达到了懒加载的效果;在进行初始化的时候会进行同步(加锁),因此没有线程安全问题;并且只有第一次进行初始化才进行同步,因此不会有效率方面的问题。

    《Java Concurrency in Practice》作者Brian Goetz在书中提到关于DCL的观点:促使DCL模式出现的驱动力(无竞争同步的执行速度很慢,以及JVM启动时很慢)已经不复存在,因而它不是一种高效的优化措施。延迟初始化占位类模式(静态内部类)能带来同样的优势,并且更容易理解。

    总结:线程安全、懒加载、效率高。

    是否推荐:可以使用。

    注:该模式还有另一种常见写法,就是把静态实例singleton的volatile修饰去掉,这种方法有线程安全方面的问题,不能使用。在我的另一篇文章有提到这个:volatile关键字详解,这里直接截取该部分内容,请见下面这个例子。

    例子:双重检测机制实现单例(没有volatile修饰)

    13c656f101c9708c4121b09dfd6a5682.png

    这段代码是单例的双重检测机制实现,相信很多人都用过,并且觉得这个代码是没问题的。在大多数情况,这段代码确实没问题,但在极端的情况下,有个隐藏的问题。

    例子分析:

    假设有两个线程同时访问这段代码,此时线程A走到15行开始初始化对象,线程B则刚走到12行进行第一次检测。这时要介绍下15行初始化这行代码,这行代码虽然只有一句话,但是被编译后会变成以下3条指令:

    2d7313ec3edb35d25028c29ae1295c8c.png

    正常情况下,这3条执行时按顺序执行,双重检测机制就没有问题。但是CPU内部会在保证不影响最终结果的前提下对指令进行重新排序(不影响最终结果只是针对单线程,切记),指令重排的主要目的是为了提高效率。在本例中,如果这3条指令被重排成以下顺序:

    ccbca0de4fe139e9f8fd36b245723c71.png

    如果线程A执行完1和3,instance对象还未完成初始化,但是已经不再指向null。此时线程B抢占到CPU资源,执行第12行的检测结果为false,则执行第19行,从而返回一个还未初始化完成的instance对象,从而出导致问题出现。要解决这个问题,只需要使用volatile关键字修饰instance对象即可。

    4.静态内部类(延迟初始化占位类)

    5c2a34c51a5dc1f16359cfff0cc217bf.png

    静态内部类(延迟初始化占位类),比较常见的一种写法。JVM将推迟SingletonHolder的初始化操作,直到开始使用这个类时才初始化,并且由于通过一个静态初始化来初始化Singleton,因此不需要额外的同步。当任何一个线程第一次调用getInstance时,都会使SingletonHolder被加载和被初始化,此时静态初始化器将执行Singleton的初始化操作。

    通过静态初始化来初始化Singleton为什么不需要额外的同步?

    在初始器中采用了特殊的方式来处理静态域(或者在静态初始化代码块中初始化的值),并提供了额外的线程安全性保证。静态初始化器是由JVM在类的初始化阶段执行,即在类被加载后并且被线程使用之前。由于JVM将在初始化期间获得一个锁,并且每个线程都至少获取一次这个锁以确保这个类已经加载,因此在静态初始化期间,内存写入操作将自动对所有线程可见。因此无论是在被构造期间还是被引用时,静态初始化的对象都不需要显式的同步。然而,这个规则仅适用于在构造时的状态,如果对象是可变的,那么在读线程和写线程之间仍然需要通过同步来确保随后的修改操作是可见的,以及避免数据破坏。

    总结:线程安全、懒加载、效率高。

    是否推荐:推荐使用、《Java Concurrency in Practice》作者Brian Goetz推荐使用的方式。

    5.枚举

    e1b4476f1d02c0d443afb634a64da5c6.png

    枚举,不是很常见的一种写法。很简洁的一种实现方式,提供了序列化机制,保证线程安全,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候。

    总结:线程安全、非懒加载、效率高。

    是否推荐:推荐使用、《Effective Java》作者Joshua Bloch推荐使用的方式。

    几种方式对比:

    acfb3a631fbc65fcbcc53f5589e0354d.png

    可能有人看了以上表格,觉得枚举有缺点,为什么Joshua Bloch还推荐使用枚举?

    这就要提到单例的破解了。普通的单例模式是可以通过反射和序列化/反序列化来破解的,而Enum由于自身的特性问题,是无法破解的。当然,由于这种情况基本不会出现,因此我们在使用单例模式的时候也比较少考虑这个问题。

    总结:

    1. 单例无论是在项目的开发中,还是面试中都是非常常见的,因此需要熟练的掌握好单例的知识。只要记住单例的三个实现要点——私有构造方法、定义静态Singleton实例、暴露实例获取方法,手写这5种实现方式相信都是信手拈来。如果是面试的时候,建议使用静态内部类的实现。
    2. 项目中具体使用哪种实现方式可以根据情况而定,个人推荐的方式是:静态内部类和枚举,我自己在项目中常见的也是这两种方式,可能静态内部类会更多一点。另外就是毕竟是两位大神推荐的,还考虑什么,直接用就是了。

    推荐阅读

    程序员囧辉:字节、美团、快手核心部门面试总结(真题解析)​zhuanlan.zhihu.com
    47b7ec4aa92144c060eca6bdb3f5e0e6.png
    程序员囧辉:4 年 Java 经验面试总结、心得体会​zhuanlan.zhihu.com
    bb621ab07566c36738d88f23e70a3a0f.png
    程序员囧辉:面试阿里,HashMap 这一篇就够了​zhuanlan.zhihu.com
    a410e88ddb8ea01b8b99ae9c50277449.png
    程序员囧辉:面试必问的线程池,你懂了吗?​zhuanlan.zhihu.com
    bb621ab07566c36738d88f23e70a3a0f.png
    程序员囧辉:BATJTMD 面试必问的 MySQL,你懂了吗?​zhuanlan.zhihu.com
    c017b0c53f2f088f521b147cfbb2c719.png
    展开全文
  • 主要介绍了Java静态内部类实现单例过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 定义静态内部类 定义获取实例的静态方法 public class Singleton { private Singleton() {} private static class SingleInstance { private static final Singleton INSTANCE = new Singleton(); } public ...
    1. 定义私有构造函数
    2. 定义静态内部类
    3. 定义获取实例的静态方法
    public class Singleton {
        private Singleton() {}
    
        private static class SingleInstance {
            private static final Singleton INSTANCE = new Singleton();
        }
    
        public static Singleton getInstance() {
            return SingleInstance.INSTANCE;
        }
    }
    
    
    展开全文
  • 实现方式 public class SingleTonInstance { private SingleTonInstance(){}; public static SingleTonInstance getInstance(){ return SingleTonHolder.sInstance; } private static class SingleTonHolder{...

    实现方式

    public class SingleTonInstance {
        private SingleTonInstance(){};
    
        public static SingleTonInstance getInstance(){
            return SingleTonHolder.sInstance;
        }
    
        private static class SingleTonHolder{
             private static final SingleTonInstance sInstance = new SingleTonInstance();
        }
    	
    	...
    }
    

    懒加载

    程序运行时外部类会被加载到内存,而静态内部类不会加载,只有当getInstance方法被调用时才加载SingleTonHolder并生成外部类SingleTonInstance的实例对象sInstance 。

    单例

    由于sInstance是静态的,程序中仅有一个实例,保证了单例。

    线程安全

    加载SingleTonHolder类时,是调用ClassLoader的loadClass方法,最终加载类的方法中其实是采用synchronize加锁的,因为sInstance是该类成员变量也是在类加载方法中初始化的,所以保证了线程安全。

     protected Class<?> loadClassOrNull(String cn, boolean resolve) {
        synchronized (getClassLoadingLock(cn)) {
            // check if already loaded
            Class<?> c = findLoadedClass(cn);
    
            return c;
        }
    
    展开全文
  • //基于初始化的线程安全的单例 class SingleTon4{ private SingleTon4(){} private static class InnerClass{ private static SingleTon4 instance= new SingleTon4(); } public static SingleTon4 ...
  • public class LazyInnerClassSingleton { private LazyInnerClassSingleton(){}; public static final LazyInnerClassSingleton getInstance(){ return LazyHolder.LAZY;... //静态内部类默认是不加载...
  • 静态内部类实现单例模式,是借助了加载器加载的特性实现的,加载器在加载时使用了synchronized关键字. 单例模式代码: public class SingletonDemo { public static void main(String[] args) { ...
  • 静态内部类 单例设计模式
  • 静态代码块和静态内部类的加载顺序:当调用外部的构造函数是,外部的静态代码块同时被加载,但是其内部不会同时被加载;当且仅当内部的静态域或其构造方法或其静态方法被调用时,内部内才被加载。 因此,...
  • 还有一种通过内部实现单例的方式,静态内部类只能访问外部的静态方法和静态属性。现在一般利用这个特性来实现单例模式。应该在初始化的时候线程是互斥的,可以完美的解决单例创建冲突的问题。 借用...
  • } } 如上,就是静态内部类创建单例。优点如下: 1. 仅在需要使用单例时调用getInstance进行单例的创建。 2. JVM虚拟机保证了静态内部类SingletonHolder的初始化只执行一次,不需要我们手动保证并发的同步。 这里...
  • 静态内部类传参单例

    2020-11-06 15:53:09
    单例 public class Singleton { private static String s; private static int i; private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private ...
  • 静态内部类实现懒汉单例模式
  • 封装的某个不包含具有具体业务含义的成员变量,是对业务动作的封装,如MVC中的各层(HTTPRequest对象以Threadlocal方式传递进来的)。2.某个具有全局意义,一旦实例化为对象则对象可被全局使用。如某个封装了...
  • 静态内部类单例模式

    2021-12-21 19:42:19
    单例模式最简单的写法就是静态内部类单例模式,如下: public class Manager { private static class ManagerHolder { private static Manager instance = new Manager(); } private Manager() { } ...
  • 单例模式通常由饱汉式和饿汉式,JDK1.8后有了匿名内部类,我们也可以对单例模式进行一个优化了 具体写法参考:单例模式有几种写法?(饱汉式和饿汉式). 匿名内部类写法 /** * 内部类,只要没有被使用,就不会初始...
  • 本篇是对现网上流传的 Kotlin 实现静态内部类单例模式的纠正,为了把原理说清楚,文章前奏可能会有些长,熟悉静态内部类单例模式原理的朋友,可以直接跳转到文章最后,直接看结果即可。 最近在整理基础库的时候,...
  • 静态内部类实现方式(也是一种懒加载方式) 要点:外部没有static属性,则不会像饿汉式那样立即加载对象只有真正调用了getInstance()才会加载静态内部类,加载时是线程安全的。sc是static final类型,保证了内存中...
  • 静态内部类单例的好处

    千次阅读 2019-03-22 15:57:20
    以前写单例这样的 public class Demo { private static Demo instance; private Demo() {} public static Demo getInstance() { if (instance == null) { synchronized (Demo.class) { ...
  • 静态内部类和非静态内部类与外部...静态内部类的初始化不依赖于外部,又因为的初始化过程中是线程安全的,所以可以实现单例模式。 而对于非静态内部类而言,由于需要依赖外部的初始化,所以实现不了单例模式 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 94,727
精华内容 37,890
关键字:

静态内部类实现单例

友情链接: CDib101.rar