Java高并发--安全发布对象

主要是学习慕课网实战视频《Java并发编程入门与高并发面试》的笔记

发布对像:使一个对象能够被当前范围之外的对象使用。

对象逸出:一种错误的发布。当一个对象还没有构造完成时,就使他被其他线程所见。

如何安全的发布对象?

  • 在静态初始化函数中初始化一个对象引用
  • 将对象的引用保存到volatile类型域或者AtomicReference中
  • 将对象的引用保存到某个正确构造对象的final类型域中
  • 将对象的引用保存到一个由锁保护的域中

单例模式

拿单例模式来说

@ThreadSafe
public class SingletonImp {
    // 饿汉模式
    private static SingletonImp singletonImp = new SingletonImp();
    // 私有化(private)该类的构造函数
    private SingletonImp() {
    }

    public static SingletonImp getInstance() {
        return singletonImp;
    }

    public static void main(String[] args) {
        System.out.println(SingletonImp.getInstance());
    }
}

上面是“饿汉模式”的实现。这个实现是线程安全的,但是不能延迟加载。也就是说这个单例在类加载时就已经初始化,而不是在需要用到他的地方再初始化的,这在一定程度上造成了资源的浪费。

单例还经常有一种叫“懒汉模式”,也就是在需要用到的时候才初始化。

@NotThreadSafe
public class SingletonImp2 {
    private static SingletonImp2 singletonImp2;

    private SingletonImp2() {}
    // 懒汉模式
    public static SingletonImp2 getInstance() {
        if (singletonImp2 == null) {
            singletonImp2 = new SingletonImp2();
        }
        return singletonImp2;
    }

    public static void main(String[] args) {
        System.out.println(SingletonImp2.getInstance());
    }
}

这个程序实现了延迟加载,但是在多线程下,可能多个线程同时执行到if (singletonImp2 == null) {}导致同时创建了多个实例对象,这是线程不安全的实现。所以就有了下面的实现

@ThreadSafe
public class SingletonImp3 {
    private static SingletonImp3 singletonImp3;

    private SingletonImp3() {}
    // 懒汉模式
    public static SingletonImp3 getInstance() {
        // 同步锁的加入
        synchronized (SingletonImp3.class) {
            if (singletonImp3 == null) {
                singletonImp3 = new SingletonImp3();
            }
        }
        return singletonImp3;
    }
}

这个实现加了同步锁,因此是线程安全的,但是在实例被创建出来后每次getInstance都会加上同步锁(这是完全没有必要的,因为if条件中的语句不会被执行到了 ),严重拖慢了程序的性能。

完全可以在第一次创建实例时才上锁,其余时候不需要,基于此思想的实现一般称为“双重检测”,如下

@NotThreadSafe
public class SingletonImp4 {
    private static SingletonImp4 singletonImp4;

    private SingletonImp4() {}

    public static SingletonImp4 getInstance() {
        // 第一次创建时才加锁
        if (singletonImp4 == null) {
            synchronized (SingletonImp4.class) {
                if (singletonImp4 == null) {
                    singletonImp4 = new SingletonImp4();
                }
            }
        }

        return singletonImp4;
    }
}

注意,这个实现是线程不安全的。究其原因,singletonImp4 = new SingletonImp4();这句其实是分三步完成的。

  1. 分配对象的内存空间

  2. 初始化对象

  3. 设置singleton变量指向刚刚分配的内存

但是由于指令重排原因,第2步和第3步可能会被交换(这个顺序在单线程下没有影响)。我们再看这个程序,注意注释部分。假设线程B执行到第一个if处,同时线程A执行到第二个if处,且由于指令重排的原因,只完成了上面三步的第1和第3步,也就是说此时singletonImp4已经被分配了内存,有值了在线程B判断时,就会跳过if直接返回singletonImp4。但是singletonImp4还没有初始化!线程B取得了还未初始化的对象,一旦使用就会出现问题。

@NotThreadSafe
public class SingletonImp4 {
    private static SingletonImp4 singletonImp4;

    private SingletonImp4() {}

    public static SingletonImp4 getInstance() {
        if (singletonImp4 == null) { // 线程B
            synchronized (SingletonImp4.class) {
                if (singletonImp4 == null) { // 线程A
                    singletonImp4 = new SingletonImp4(); // 线程A
                }
            }
        }

        return singletonImp4;
    }
}

考虑到volatile关键字可以禁止指令重排,声明singletonImp4时,添加volatile就可以解决这个问题了。

 private static volatile SingletonImp4 singletonImp4;

这样volatile + double check才是线程安全的实现。

Java高并发--安全发布对象的更多相关文章

  1. java高并发系列 - 第13天:JUC中的Condition对象

    本文目标: synchronized中实现线程等待和唤醒 Condition简介及常用方法介绍及相关示例 使用Condition实现生产者消费者 使用Condition实现同步阻塞队列 Object对 ...

  2. Java高并发--线程安全策略

    Java高并发--线程安全策略 主要是学习慕课网实战视频<Java并发编程入门与高并发面试>的笔记 不可变对象 发布不可变对象可保证线程安全. 实现不可变对象有哪些要注意的地方?比如JDK ...

  3. Java高并发综合

    这篇文章是研一刚入学时写的,今天整理草稿时才被我挖出来.当时混混沌沌的面试,记下来了一些并发的面试问题,很多还没有回答.到现在也学习了不少并发的知识,回过头来看这些问题和当时整理的答案,漏洞百出又十分 ...

  4. [ 高并发]Java高并发编程系列第二篇--线程同步

    高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...

  5. 【实战Java高并发程序设计 7】让线程之间互相帮助--SynchronousQueue的实现

    [实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...

  6. 【实战Java高并发程序设计 5】让普通变量也享受原子操作

    [实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...

  7. 【实战Java高并发程序设计 4】数组也能无锁:AtomicIntegerArray

    除了提供基本数据类型外,JDK还为我们准备了数组等复合结构.当前可用的原子数组有:AtomicIntegerArray.AtomicLongArray和AtomicReferenceArray,分别表 ...

  8. 【实战Java高并发程序设计 3】带有时间戳的对象引用:AtomicStampedReference

    [实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference AtomicReference无法解决上述问题的根 ...

  9. 【实战Java高并发程序设计 1】Java中的指针:Unsafe类

    是<实战Java高并发程序设计>第4章的几点. 如果你对技术有着不折不挠的追求,应该还会特别在意incrementAndGet() 方法中compareAndSet()的实现.现在,就让我 ...

随机推荐

  1. DOS 命令 os系统(windows)

    一.cd 相关操作 1."cd .. "or "cd ..\" --返回上一级 2.cd E:\Python -- 进入目录 二.dir --drectory ...

  2. Python面向对象2:类与对象的成员分析及self

    # 3. 类和对象的成员分析- 类和对象都可以存储成员,成员可以归类所有,也可以归对象所有- 类存储成员时使用的是与类关联的一个对象- 独享存储成员是是存储在当前对象中- 对象访问一个成员时,如果对象 ...

  3. SpringBoot 项目打包后运行报 org.apache.ibatis.binding.BindingException

    今天把本地的一个SpringBoot项目打包扔到Linux服务器上,启动执行,接口一访问就报错,但是在本地Eclipse中启动执行不报错,错误如下: org.apache.ibatis.binding ...

  4. 微信小程序合法域名配置-不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书

    微信小程序合法域名配置-不校验合法域名.web-view(业务域名).TLS 版本以及 HTTPS 证书 很多教程说按照以上方式调用即可.但是当我们在程序中实际调用以上程序时,就会报错, http:/ ...

  5. 《http权威指南》读书笔记9

    概述 最近对http很感兴趣,于是开始看<http权威指南>.别人都说这本书有点老了,而且内容太多.我个人觉得这本书写的太好了,非常长知识,让你知道关于http的很多概念,不仅告诉你怎么做 ...

  6. document.getElementById 和 document.getElementsByClassName获取DOM元素的区别

    想必小伙伴们对于 JS 获取DOM的几种方法早已烂熟于心,了然于胸,   尤其是 document.getElementById 和 document.getElementsByClassName, ...

  7. Kali学习笔记32:Maltego、Exiftool

    有段时间没学Kali里面的工具了 以前做信息收集的时候呢,忘记了两个很强大的工具:Maltego.Exiftool 先来看看Maltego: 这个工具不仅可以方便地收集DNS信息等等,强大地地方还在于 ...

  8. Kali学习笔记6:二层发现

    先介绍下ARPING命令: arping命令是用于发送ARP请求到一个相邻主机的工具 arping使用arp数据包,通过PING命令检查设备上的硬件地址.能够测试一个IP地址是否是在网络上已经被使用, ...

  9. [Swift-2019力扣杯春季初赛]4. 从始点到终点的所有路径

    给定有向图的边 edges,以及该图的始点 source 和目标终点 destination,确定从始点 source 出发的所有路径是否最终结束于目标终点 destination,即: 从始点 so ...

  10. rabbitmq在ios中实战采坑

    1. rabbitmq在ios中实战采坑 1.1. 问题 ios使用rabbitmq连接,没过多久就断开,并报错.且用android做相同的步骤并不会报错,错误如下 Received connecti ...