zoukankan      html  css  js  c++  java
  • JAVA单例模式

    一:懒汉模式,线程不安全

    懒加载(在需要的时候创建对象)

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

    二:懒汉模式,线程安全

    /**
     * 懒汉模式,线程安全
     * */
    public class Singleton2 {
        private static Singleton2 singleton2;
        private Singleton2(){}
        public static synchronized Singleton2 getInstance(){
            if(singleton2==null){
                singleton2=new Singleton2();
            }
            return singleton2;
        }
    }

    效率极为低下,不建议使用

    三:饿汉模式,线程安全

    public class Singleton3 {
        private static Singleton3 singleton3=new Singleton3();
        private Singleton3(){}
        public static synchronized Singleton3 getInstance(){
            return singleton3;
        }
    }

    没有达到懒加载的目的,不推荐

    四:饿汉模式,线程安全

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

    等同于第三种

    五:静态内部类,线程安全

    public class Singleton5 {
        private Singleton5(){}
        public static Singleton5 getInstance(){
            return SingletonHolder.INSTANCE;
        }
        private static class SingletonHolder{
            private static Singleton5 INSTANCE=new Singleton5();
        }
    }

    与第三第四种的区别是:前者在类加载时就会创建实例,假如实例耗费内存,则是一种不明智的选择,后者则会懒加载

    第六种:枚举类,线程安全

      public enum Singleton6 {
        INSTANCE;
        public void whateverMethod(){}
        }

    安卓推荐,不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象

    第七种:双重校验锁,线程安全

    public class Singleton7 {
    
        private Singleton7() {}; // 私有化构造方法
        private static volatile Singleton7 singleTon = null;
        public static Singleton7 getInstance() {
            // 第一次校验
            if (singleTon == null) {
                synchronized (Singleton7.class) {
                    // 第二次校验
                    if (singleTon == null) {
                        singleTon = new Singleton7();
                    }
                }
            }
            return singleTon;
        }
    }

    分析:

      第一次校验:由于单例模式只需要创建一次实例,如果后面再次调用getInstance方法时,则直接返回之前创建的实例,因此大部分时间不需要执行同步方法里面的代码,大大提高了性能。如果不加第一次校验的话,那跟上面的懒汉模式没什么区别,每次都要去竞争锁。

      第二次校验:如果没有第二次校验,假设线程t1执行了第一次校验后,判断为null,这时t2也获取了CPU执行权,也执行了第一次校验,判断也为null。接下来t2获得锁,创建实例。这时t1又获得CPU执行权,由于之前已经进行了第一次校验,结果为null(不会再次判断),获得锁后,直接创建实例。结果就会导致创建多个实例。所以需要在同步代码里面进行第二次校验,如果实例为空,则进行创建。

      为什么要加volatile关键字呢?

      需要注意的是,private static volatile SingleTon3 singleTon=null;需要加volatile关键字,否则会出现错误。问题的原因在于JVM指令重排优化的存在。在某个线程创建单例对象时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了,然而该对象可能还没有初始化。若紧接着另外一个线程来调用getInstance,取到的就是状态不正确的对象,程序就会出错。

      详细解释一下为什么要使用volatile

    public class Singleton {
        private static Singleton s;
        private Singleton(){};
        public static Singleton getInstance() {  //1
            if(s == null) { //2
                synchronized (Singleton.class) { //3
                    if(s == null) { //4
                        s = new Singleton(); //5
                    }
                }
            }
            return s; //6
        }
    }

    以前不了解为什么需要volatile关键字,后来发现在并发情况下,如果没有volatile关键字,在第5行会出现问题
    对于第5行 s = new Singleton(); //5
    可以分解为3个步骤:

    1 memory=allocate();// 分配内存 相当于c的malloc
    2 ctorInstanc(memory) //初始化对象
    3 s=memory //设置s指向刚分配的地址

    上面的代码在编译器运行时,可能会出现重排序 从1-2-3 排序为1-3-2

    如此在多线程下就会出现问题

    例如现在有2个线程A,B

    线程A在执行第5行代码时,B线程进来,而此时A执行了 1和3,没有执行2,此时B线程判断s不为null (语句2)直接返回一个未初始化的对象,就会出现问题

  • 相关阅读:
    网站架构探索(3)负载均衡的方式
    架构师之路(6)OOD的开闭原则
    也谈IT人员流失问题 王泽宾
    技术体系的选择之Java篇
    网站架构探索(2)CDN基本常识
    设计模式之单例模式
    网站架构探索(1)序言 王泽宾
    架构师之路(39)IoC框架
    发展之道:简单与专注
    修me30打印机
  • 原文地址:https://www.cnblogs.com/xhlwjy/p/11250808.html
Copyright © 2011-2022 走看看