双重检查锁定与单例

代码星冰乐

专注成就未来

首页 归档 关于

双重检查锁定与单例

Jul 2, 2019 | haifeiWu | Java | 阅读
文章目录
  1. 1. 双重检查锁的单例
  2. 2. 解决方案

作 者:haifeiWu
原文链接:https://www.hchstudio.cn/article/2019/2b9/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。


由于版权原因,请阅读原文 --> 双重检查锁定与单例

关注我们

作 者:haifeiWu
原文链接:https://www.hchstudio.cn/article/2019/2b9/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

作 者:haifeiWu
原文链接:https://www.hchstudio.cn/article/2019/2b9/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

对于单例模式,相信大多数人都可以写出好几种实现方法,懒汉,饿汉等等,然而小小单例真要写好,写的完全正确也并非易事。

双重检查锁的单例

下面是我们经常使用的一种单例的实现,也就是双重检查所的实现方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Singleton {
private static Singleton instance;

private Singleton() {

}

public Singleton getInstance() {
if (null == instance) {
synchronized (Singleton.class) {
if (null == instance) {
instance = new Singleton(); // error
}
}
}
return uniqueSingleton;
}
}

让我们来看一下这个代码是如何工作的:首先当一个线程发出请求后,会先检查instance是否为null,如果不是则直接返回其内容,这样避免了进入synchronized块所需要花费的资源。其次,如果两个线程同时进入了第一个if判断,那么他们也必须按照顺序执行 synchronized 块中的代码,第一个进入代码块的线程会创建一个新的 Singleton 实例,而后续的线程则因为无法通过if判断,而不会创建多余的实例。

但还有一个问题,在有些情况下,通过这种方式拿到的Singleton对象,可能是错误的 。

回顾我们new对象的3个步骤

  • 1,分配内存空间

  • 2,初始化对象

  • 3,将对象指向刚分配的内存空间

但jvm在指令优化时,会出现步骤2和3对调的情况,比如线程1在经过俩层为 null 判断后,进入 new 的动作,在还没有初始化对象时,就返加了地址值,线程2在第一个为 null 判断时,因为对象已经不为空,那么就直接返回了对象。然而当线程2打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。

解决方案

对于上面的问题,有两种解决方案

1,使用 volatile 关键词主要可以保证代码的执行顺序不受 jvm 重排序影响。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Singleton {
private volatile static Singleton instance;

private Singleton() {
}

public Singleton getInstance() {
if (null == instance) {
synchronized (Singleton.class) {
if (null == instance) {
instance = new Singleton(); // error
}
}
}
return instance;
}
}

2,通过内部类实现多线程环境中的单例模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton {        

private Singleton() {
}

private static class SingletonContainer {
private static Singleton instance = new Singleton();
}

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

关注我们

作 者:haifeiWu
原文链接:https://www.hchstudio.cn/article/2019/2b9/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

分享
Java
[译] 为什么String在Java中是不可变的LRU 算法
微信关注我们
分类
  • Android8
  • Go4
  • Java59
  • Kafka,Java1
  • Kotlin2
  • Linux1
  • MapReduce1
  • Python2
  • Raft1
  • Redis1
  • ThreadPoolExecutor1
  • go1
  • 工具1
  • 总结8
  • 旅游日记1
标签
Nginx ChanghuiN haifeiWu Android Java 设计模式 hexo Kotlin 算法 MySQL 源码解析 Python Redis golang web Kafka 配置中心 总结 性能优化 旅游日记 Shell Go 问题排查 译文 Docker Spring Boot 工具 学习笔记 WebFlux 性能测试 go 散列表 源码 netty Raft
最近文章
  • Kafka的日志复制机制
  • 从20到21
  • go 并发编程
  • 【译】了解Linux CPU负载-您何时应该担心?
  • Zookeeper 与分布式锁
  • 基于Redis的分布式锁到底安全吗?
  • 【译】Raft 学生指南
  • ThreadPoolExecutor 的简单梳理
  • MapReduce 的简单实现
  • 使用 Map 实现策略模式
福利专区
    免费SSL证书
      阿里云红包
        腾讯云专属福利
        Copyright © 2021 代码星冰乐. Powered by ChanghuiN. 版权所有 晋ICP备15001365号
        特别感谢: 云服务器服务商 、 CDN 服务商