栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

HeadFirst设计模式学习笔记(2)——单例模式

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

HeadFirst设计模式学习笔记(2)——单例模式

文章目录
        • 特征
        • 引入
        • 多线程
        • 如何改善多线程?
          • 方法一、“急切”创建实例
          • 方法二、“双重检查加锁”,在getInstance()中减少使用同步
        • 面试常用
          • 单例模式虽然只有一个类,但是可能会导致多线程难以同步之类的问题,它适合什么场景呢?
          • 难道不能创建一个类,把所有的方法和变量都定义为静态的,把类直接当成一个单件?
          • 那么类加载器呢?听说两个类可能有机会各自创建自己的单例实例。
          • 想把单件类设计成超类,设计出子类,究竟可以不可以继承单件类?
          • 为什么全局变量比单例模式差?
        • 要点

特征

类图上只有一个类

引入

很多情况下,只有一个对象,比如线程池(threadpool)、缓存(cache)、处理偏好设置和注册表(registry)的对象。这类对象只有一个实例,多个实例反而会造成很多问题。

  • 单例对象的构造器是私有的。
  • 有一个静态方法getInstance(),调用它来开启工作
  • if(uniqueInstance == null)表示如果我们不需要这个实例,他就永远不会产生,这就“延迟实例化”(lazy instantiaze)
public class SingLeton{
    //利用一个静态变量来记录SingLeton类的唯一实例
    private static SingLeton uniqueInstance;
    private SingLeton(){}
    public static SingLeton getInstance(){
        if(uniqueInstance == null){
            uniqueInstance = new SingLeton();
        }
        return uniqueInstance;
    }
    //其他方法
}
多线程

多线程可能会造成一定的问题

如何处理多线程呢?
public static synchronized SingLeton getInstance()

public class SingLeton{
    //利用一个静态变量来记录SingLeton类的唯一实例
    private static SingLeton uniqueInstance;
    private SingLeton(){}
    public static synchronized SingLeton getInstance(){
        if(uniqueInstance == null){
            uniqueInstance = new SingLeton();
        }
        return uniqueInstance;
    }
    //其他方法
}

通过新增synchronized关键字到getInstance()方法中,迫使每个线程进入方法前,要先等别的线程离开该方法,即同一时间不会有多个线程进入该方法。uniqueInstance在设置好后,就不需要同步了。频繁引用getInstance()程序外加使用同步会导致程序执行效率下降100倍。

如何改善多线程? 方法一、“急切”创建实例

如果程序总是使用单例或者创建和运行时程序负担较小,可以“急切”创建实例,而不是用延迟实例化的做法。JVM保证任何线程访问uniqueInstance静态变量前,一定先创建此实例。

public class SingLeton{
    //利用一个静态变量来记录SingLeton类的唯一实例
    private static SingLeton new SingLeton();
    private SingLeton(){}
    public static SingLeton getInstance(){
        return uniqueInstance;
    }
    //其他方法
}

方法二、“双重检查加锁”,在getInstance()中减少使用同步

首先检查是否创建了实例,如果尚未创建,“才”进行同步。

public class SingLeton {
    //利用一个静态变量来记录SingLeton类的唯一实例
    private volatile static SingLeton uniqueInstance;

    private SingLeton() {
    }

    public static SingLeton getInstance() {
        if (uniqueInstance == null) {
            synchronized (SingLeton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new SingLeton();
                }
            }
        }
        return uniqueInstance;
    }
}

volatile

某个属性被多个线程共享,其中有一个线程修改了此属性,其他线程可以立即得到修改后的值
两次检查uniqueInstance为null,先进入同步再确定null才进行实例化。

面试常用 单例模式虽然只有一个类,但是可能会导致多线程难以同步之类的问题,它适合什么场景呢?

A1:正确使用单例模式的方法有三种,使用同步机制,“急切”创建单例,或者双检测+同步机制。当需要控制实例个数的时候还是应当先用单例模式。

难道不能创建一个类,把所有的方法和变量都定义为静态的,把类直接当成一个单件?

A2: 当这样做时,由于构造方法是静态的,容易产生和初始化有关的bug,尤其是多个类时,更容易产生问题,所以建议使用对象的单例。

那么类加载器呢?听说两个类可能有机会各自创建自己的单例实例。

A3: 不同的类加载器会导致同一个类被加载多次,多个单件并存的情况。所以如果程序中有多个类加载器,又同时使用了单件模式,需要自行指定同一个类加载器。

想把单件类设计成超类,设计出子类,究竟可以不可以继承单件类?

A4: 继承单件类遇到的问题就是构造器是私有的。如果修改构造器的访问权限,单件的实现是利用静态变量,直接继承会导致所有的派生类共享同一个实例变量。

为什么全局变量比单例模式差?

单例模式的作用是确保只有一个实例并提供全局访问。全局变量实际上就是对对象的静态引用,这样的情况下就不能是根据我们需要来创建对象,“延迟实例化”和“急切”创建就是个例子,其中“急切”创建实例不能考虑到我们的需要。

要点

实现单件模式需要私有的构造器,一个静态方法和一个静态变量。
如果使用多个类加载器,可能会导致单件失效而产生多个实例。
结束啦~

我讲清楚了吗?觉得不错的话点个~

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1019996.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号