• java单例类的几种实现


    一,最简单的方式

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

    首先构造函数声明为private,防止被外部创建该类的实例。声明一个static的成员变量instance并分配实例,当Singleton类被加载时,instance便会被创建,可以通过静态方法getInstance方法获取到该实例。
    优点是实现简单,且没有线程安全问题。缺点是Singleton被引用时instance实例就已经创建,即使实例并没有用到。

    二,懒加载的单例类

    public class Singleton{
        private Singleton(){};
        private static Singleton instance = null;
        public static synchronized Singleton getInstance(){
            if(instance == null){
                instance = new Singleton();
            }
            return instance;
        }
    }

    因为instance初始值为null,所以在Singleton被加载的时候,并不会实例化,只有调用getInstance方法的时候才会创建实例。为了防止多线程并发调用getInstance方法时instance被多次创建,所以使用synchronized关键字进行线程同步。
    该实现缺点是,每次调用getInstance方法都要进行线程同步,影响并发量。

    三,改进懒加载的单例类

    public class Singleton{
        private Singleton(){};
        private static volatile Singleton instance = null;
        public static Singleton getInstance(){
            if(instance == null){
                synchronized(Singleton.class){
                    if(instance == null){
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

    通过双检索的方式进行优化,只有在intance没有值时才进行线程同步,此后会直接返回实例。需要注意的是,在synchronized代码块中需要再判断一次instance是否为null,防止多个线程同时通过了第一个为null的判断。
    还需要注意的是,instance需要volatile修饰,防止指令重排序导致的错误。
    volatile除了保证线程缓存及时同步到主内存并清理其他线程缓存的值,还有一个作用就是防止指令重排序。 instance = new Singleton() 这行代码编译后会拆分成三个指令,可以理解成如下代码:
    1,Singleton temp = malloc(); // 分配内存
    2,constructor(temp); // 调用构造函数对分配的内存进行初始化
    3,instance = temp; // 初始化完成的内存地址赋值给instance
    编译器为了优化指令,重排序后,可能会变成了下面的代码:
    1,Singleton temp = malloc(); // 分配内存
    2,instance = temp; // 初始化完成的内存地址赋值给instance
    3,constructor(instance); // 调用构造函数对分配的内存进行初始化
    如果代码执行了上面的第二步,instance已经赋值不为null,但并没有初始化,这是如果第二个线程调用getInstance方法就会直接获得instance,调用instance时引发错误。volatile可以防止重排序。

    四,通过内部类实现单例

    public class Singleton{
        private static class SingletonHolder{
            private static Singleton instance = new Singleton();
        }
    
        private Singleton(){};
        private static volatile Singleton instance = null;
        public static Singleton getInstance(){
            return SingletonHolder.instance;
        }
    }

    Singleton的实例被静态内部类SingletonHolder持有,只有在调用getInstance方法时,SingletonHolder才会被加载,instance被实例化。既实现懒加载,也不会有线程安全问题。

  • 相关阅读:
    python全栈开发从入门到放弃之socket并发编程之协程
    python全栈开发从入门到放弃之socket并发编程多线程GIL
    python全栈开发从入门到放弃之socket并发编程多线程
    python全栈开发从入门到放弃之socket并发编程多进程
    python全栈开发从入门到放弃之socket网络编程基础
    python全栈开发从入门到放弃之异常处理
    python全栈开发从入门到放弃之面向对象反射
    python全栈开发从入门到放弃之面向对象的三大特性
    转:经典ACM算法
    反射在Java Swing中的应用
  • 原文地址:https://www.cnblogs.com/FuzhePan/p/9319138.html
Copyright © 2020-2023  润新知