IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 单例模式的几种实现方式 -> 正文阅读

[Java知识库]单例模式的几种实现方式

一、懒汉式单例模式

懒汉式单例模式,即是在需要用到该对象的时候才去进行初始化,代码如下:

public class Singleton {
    private static final Singleton instance;

    private Singleton () {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }

        return instance;
    }
}

此种单例模式在单线程情况下可以很正常的运行,但是在多线程的情况下就可能导致同时创建多个 instance 实例:

TimeThread AThread B
1调用 getInstance,此时 instance 为 null
2调用 getInstance,此时 instance 为 null
3创建 Singleton 对象 instanceA 并返回
4创建 Singleton 对象 instanceB 并返回

此时 instance 对象被实例化了两次,不符合单例模式的初衷。

线程安全的懒汉式单例模式

为了确保线程安全,我们可以对 getInstance 方法加锁:

public class Singleton {
    private static final Singleton instance;

    private Singleton () {}

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

        return instance;
    }
}

这样做确实可以保证在多线程情况下的线程安全,但是这样子每次调用 getInstance 方法去获取 instance 对象的时候都会进行加锁操作,会造成比较大的开销,而实际上只有在对 instance 对象进行初始化的时候才需要加锁。

二、双重检测锁实现单例模式

//错误的双重检测锁
public class Singleton {
    private static final Singleton instance;

    private Singleton () {}

    public static synchronized Singleton getInstance() {
        if (instance == null) {  //第一次空检测
            synchronized (Singleton.class) {
                if (instance == null) {  //第二次空检测
                    instance = new Singleton();  //实例化对象
                }
            }
        }

        return instance;
    }
}

我们将加锁的位置后移,在检查变量是否已经初始化的时候不加锁,在变量未被初始化,需要去初始化的时候才加锁;这样子后续调用 getInstance 方法或取 instance 对象时不需要再加锁,减少了性能消耗。

  • 为什么要对 instance 进行两次空检测:可能存在多个线程同时通过了第一次空检测的情况,此时其中一个线程获得锁并初始化对象成功,那么第二次空检测可以避免其它通过第一次检测的线程重复初始化对象。

但是正如代码中注释的,这种加锁方式是错误的,为什么呢?

在代码中实例化对象的那一行实际上由三个步骤组成:

  1. 分配内存空间
  2. 初始化对象
  3. 将对象指向刚分配的内存空间

在一些编译器中为了提高运行速度可能会进行指令重排序,那么执行顺序会变成:

  1. 分配内存空间
  2. 将对象指向刚分配的内存空间
  3. 初始化对象

那么在多线程情况下可能会发生下面这种情况:

TimeThread AThread B
1调用 getInstance,检测到 instance 为 null
2获取锁
3检测到 instance 为 null
4为 instance 分配内存空间
5将 instance 指向刚分配的内存空间
6检测到 instance 不为 null,返回 instance
7访问 instance
8初始化 instance 对象

在这种情况下,线程 B 访问了一个初始化未完成的 instance 的对象,会发生错误,因此我们需要添加 volatile 关键字来避免指令重排序,因此正确的双重加锁代码如下:

//正确的双重检测锁
public class Singleton {
    private volatile static final Singleton instance;

    private Singleton () {}

    public static synchronized Singleton getInstance() {
        if (instance == null) {  //第一次空检测
            synchronized (Singleton.class) {
                if (instance == null) {  //第二次空检测
                    instance = new Singleton();  //实例化对象
                }
            }
        }

        return instance;
    }
}

三、饿汉式单例模式

饿汉式单例模式,即是在类加载的时候就创建并初始化单例对象,其代码如下:

public class Singleton {
    private static final Singleton instance = new Singleton();

    private Singleton () {}

    public static Singleton getInstance() {
        return instance;
    }
}

四、静态内部类实现单例模式

使用静态内部类利用了 Java 静态内部类的特性:Java 加载外部类的时候,不会创建内部类的实例,只有在外部类使用到内部类的时候才会创建内部类实例。其代码如下:

public class Singleton {
    private Singleton () {}

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

五、枚举实现单例模式

使用枚举实现单例模式利用了枚举的特性:全局唯一。其代码如下:

public enum Singleton {
    INSTANCE;
}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-13 21:36:26  更:2022-03-13 21:38:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:31:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码