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

    定义

    单例模式是限制类的实例只有一个的设计模式。

    代码

    单线程下的单例模式代码

    public class SimpleSingleton {
        private static SimpleSingleton simpleSingleton;
    ​
        private SimpleSingleton() {}
    ​
        public static SimpleSingleton getInstance() {
            // 如果 simpleSingleton 未进行实例化,则创建实例,之后都使用此实例
            if (simpleSingleton == null) {
                simpleSingleton = new SimpleSingleton();
            }
            return simpleSingleton;
        }
    }

    但是如果在多线程的情况下,可能出现在第一个线程 A 线程判断simpleSingleton为空时,进入simpleSingleton = new Simpleton();部分进行代码实例化,但在 A 线程还未实例化结束时,另一个线程 B 线程进行判断simpleSingleton是否为空,此时由于 A 线程还没有实例化赋值给simpleSingleton,所以其仍未null,这是 B 线程也会进入执行simpleSingleton = new Simpleton();,这样就会导致出现不同的实例,所以这种方法只能用在单线程的情况下。

    多线程下的单例模式代码

    多线程下最简单的方式是对getInstance()方法整个加上synchronized修饰符,保证每次只有一个线程能进入此方法。但是这样做会很影响性能,我们应该尽量减小锁作用的范围,所以最好采用如下的双重加锁方式:

    public class SynchronizedSingleton {
    ​
        private static SynchronizedSingleton singleton;
    ​
        private SynchronizedSingleton() {}
    ​
        public static SynchronizedSingleton getInstance() {
            if (singleton == null) {
                synchronized (SynchronizedSingleton.class) {
                    if (singleton == null) {
                        singleton = new SynchronizedSingleton();
                    }
                }
            }
            return singleton;
        }
    }

    之所以进行第二次singleton == null判断是因为在线程 A 在第一次进行判断为null后获得锁进行实例化,在实例化未完成时,B 线程判断仍为null,这是由于获得不了锁,所以等待,在 A 线程创建实例后释放了锁,这时 B 线程获得锁并执行,如果此时不进行第二次的singleton == null判断,则 B 线程也会创建一个新的实例,导致单例模式出现问题;而如果进行第二次判断,则会得知singleton已经被实例化,就不会再创建新的实例。

    目前一切看起来都很美好,但是仍然有一个问题需要解决。

    创建对象实例可以分为三个步骤:

    1. 分配内存

    2. 调用构造函数

    3. 将对象指向分配的内存地址

    之前的代码如果依此顺序执行,则不会有问题。但是为了提高性能,编译器和处理器常常会对指令进行重排序,这时如果步骤 2 和步骤 3 的顺序颠倒了,先将对象指向分配的内存地址,后执行构造函数那么就会出现问题。当 A 线程将对象指向分配的内存地址,但还未执行构造函数的时候, B 线程进入,判断对象不为空,则将对象引用返回,这时如果使用此引用,则会出现问题。

    目前有三个解决方法:

    1. 给静态实例属性加上 volatile关键字(需要 JDK 1.5 及之后版本)
    private static volatile SynchronizedSingleton singleton;

    volatile 关键字可以保证对 volatile 变量的操作不会进行重排序。

    2. 使用单个元素的枚举类型(需要 JDK 1.5 及之后版本)
    public enum Singleton {
        INSTANCE;
    }
    3. 使用子类,由 JVM 保证单例
    public class InnerClassSingleton {
        public static Singleton getInstance() {
            return Singleton.singleton;
        }
    ​
        private static class Singleton {
            static Singleton singleton = new Singleton();
        }
    }

    类的静态属性只会在第一次加载的时候初始化一次,同时 JVM 保证在初始化的过程中(未完成时)无法被使用。

     

  • 相关阅读:
    图论基础
    排序二叉树的中序遍历
    先序遍历序列和中序遍历序列,输出该二叉树的后序遍历序列
    数据结构实验之 二叉树的建立与遍历
    c++ 头文件 及 sort 和 vector简单介绍
    最短路径(Floyd 模板题)
    最小生成树(kruskal模版 模板)
    基于邻接矩阵的广度优先搜索遍历(BFS)
    [SCOI2015]国旗计划
    [HNOI2015]开店
  • 原文地址:https://www.cnblogs.com/zawier/p/6931002.html
Copyright © 2011-2022 走看看