zoukankan      html  css  js  c++  java
  • 单例 ------ JAVA实现

    单例:只能实例化一个对象,使用场景比如打印机。

    最推荐的是采用饿汉式;双重校验锁用到了大量的语法,不能保证这些语法在所用场合一定没问题,所以不是很推荐;总之简单的才是最好的,就饿汉式!!!

    C++ 创建变量可以通过 类名 对象名,但是 JAVA 不行

    C++ new 出来的对象需要手动回收,但是 JAVA 可以自动回收

    C++ new 出来的对象需要用指针接收,但是 JAVA 对象变量就可以接收,或者说 JAVA 的对象变量就是指针

    1、(懒汉,线程安全 ------ 不推荐)

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

     说明:在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的

    2、(饿汉 ------ 推荐)

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

    说明:instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载。所以,会出现在使用对象前就创建好,占用了资源

    3、(静态内部类 ------ 推荐)

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

    说明:第三种和第二种的区别在于,第三种类被装载也不会实例化对象,一定要调用 getInstance() 才会实例化,而且只实例化一次。

    4、(双重校验锁 ------ 一般推荐<理解麻烦>)

    public class Singleton {
        private volatile static Singleton uniqueInstance;
        private Singleton (){}
        public static Singleton getSingleton() {
        if (uniqueInstance == null) { // #1
            synchronized (Singleton.class) { // #2
            if (uniqueInstance == null) { // #3
                uniqueInstance = new Singleton();  // #4
            }
            }
        }
        return singleton;
        }
    }

    说明:和第一种方式比,这种方式只会第一次调用创建对象时使用同步锁

    对象singleton 用 volatile 修饰,原因举例如下:

    1. thread2进入#1, 假设这时子线程的uniqueInstance为空(java内存模型会从主线程拷贝一份uniqueInstance=null到子线程thread2),然后thread2让出CPU资源给thread3
    2. thread3进入#1, 这时子线程的uniqueInstance还是为空(java内存模型会从主线程拷贝一份uniqueInstance=null到子线程thread3),然后thread3让出CPU资源给thread2
    3. thread2会依次执行#2,#3,#4,最终在thread2里面实例化了uniqueInstance(由于是volatile修饰的变量,会马上同步到主线程的变量去),thread2执行完毕让出CPU资源给thread3
    4. thread3接着#1跑下去,跑到#3的时候,会又一次从主线程拷贝一份uniqueInstance!=null回来,所以thread3就不会实例化对象

      上面解释的是 volatile 保证变量可见性的体现,使每个线程得到的变量来源都是唯一的;同时 volatile 还能保证变量的有序性," new Singleton "的执行其实是分三步走,第一:分配内存,第二:执行构造函数,第三:赋值,准确的说应该是引用;如果没有 volatile 修饰,在保证结果的情况下(单线程,或者是本线程中)编译器可能会打乱执行顺序,如果有另外一个线程在执行 #1 判断变量不为nll,但是此变量没有执行构造函数,获取的对象是不完整的,程序就可能发生错误。java的volatile顺序性方面比C++强,能保证此顺序执行。

       volatile 防止编译器把两个 if 优化成一个 if 。

    疑问:

    1、如果赋值不是原子操作,会不会出现,没完全赋值完成就被另外一个线程使用了

  • 相关阅读:
    2019春总结作业
    第十周作业
    第九周作业
    第八周作业
    第七周作业
    202103226-1 编程作业
    1 20210309-2 阅读任务
    1 20210309-1 准备工作
    课程总结
    第十三周总结
  • 原文地址:https://www.cnblogs.com/god-of-death/p/7750336.html
Copyright © 2011-2022 走看看