zoukankan      html  css  js  c++  java
  • 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被实例化。既实现懒加载,也不会有线程安全问题。

  • 相关阅读:
    前端 jquery获取当前页面的URL信息
    PHP合并数组及去重,PHP检测一个数组中有几个相同的
    ThinkPHP缓存技术(S(),F(),查询缓存,静态缓存)
    php 检测url
    ThinkPHP函数详解:F方法
    图片查看器(图片放大缩小功能)
    Thinkphp路由配置和静态缓存规则【原创】
    mySQL-CRUD操作(数据库的增删改查)练习题
    mySQL CRUD操作(数据库的增删改查)
    初识mySQL(关系型数据库)
  • 原文地址:https://www.cnblogs.com/FuzhePan/p/9319138.html
Copyright © 2011-2022 走看看