发布对象

发布对象:是指使一个对象能够被当前范围之外的代码所使用。

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

我们经常需要发布一些对象例如通过类的非私有方法返回对象的引用,或者通过共有静态变量发布对象。

简单例子:

@Slf4j
@NotThreadSafe
public class UnsafePublish {private String[] states = {"a", "b", "c"};public String[] getStates() {return states;}public static void main(String[] args) {UnsafePublish  unsafePublish = new UnsafePublish();log.info("{}", Arrays.toString(unsafePublish.getStates()));unsafePublish.getStates()[0] = "d";log.info("{}", Arrays.toString(unsafePublish.getStates()));}
}

输出:

15:42:12.937 [main] INFO com.vincent.example.publish.UnsafePublish - [a, b, c]
15:42:12.942 [main] INFO com.vincent.example.publish.UnsafePublish - [d, b, c]

为什么要做这个例子?UnsafePublish通过getState()方法发布了一个属性,在类的任何外部线程都可以访问这个属性,这样发布对象是不安全的,因为我们无法确定其他线程会不会修改这个属性,从而其他线程访问这个属性时,它的值是不确定的,这样发布的对象就是线程不安全的。

对象逸出

先看一个例子:

@Slf4j
@NotThreadSafe
@NotRecommend
public class Escape {private int thisCannBeEscape = 0;public Escape() {new InnerClass();}private class InnerClass {public InnerClass() {log.info("{}",Escape.this.thisCannBeEscape);}}public static void main(String[] args) {new Escape();}
}

这个例子中定义了一个内部类InnerClass,这个内部类的中引用了外部类的一个引用,有可能在对象没有正确被构造之前就被发布,有可能有不安全的因素在里面。

安全的发布对象

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

懒汉模式

/*** 懒汉模式* 单例的实例在第一次使用时进行创建*/
public class SingletonExample1 {//私有构造函数private SingletonExample1(){}//单例对象private static SingletonExample1 instance = null;//静态工厂方法public static SingletonExample1 getInstance(){if(instance == null){instance = new SingletonExample1();}return instance;}
}

上面这段代码在单线程的情况下没有问题,在多线程的情况下会有问题,问题出现在代码if(instance ==null){instance = new SingletonExample1();}return instance;部分。两个线程可以同时访问这段代码,因此这段代码有可能会被调用两次,这样两个线程拿到的实例可能是不一样的。这样写是线程不安全的。

懒汉模式-线程安全

@ThreadSafe
@NotRecommend
public class SingletonExample3 {//私有构造函数private SingletonExample3(){}//单例对象private static SingletonExample3 instance = null;//静态工厂方法public static synchronized SingletonExample3 getInstance(){if(instance == null){instance = new SingletonExample3();}return instance;}
}

这样的懒汉模式是线程安全,但是却带来了性能上的开销。而这个开销是我们不希望的。因此并不推荐这种写法。

懒汉模式-双重同步锁单例模式


/*** 懒汉模式 -> 双重同步锁单例模式* 单例的实例在第一次使用时进行创建*/
@NotThreadSafe
@ThreadSafe
@NotRecommend
public class SingletonExample4 {//私有构造函数private SingletonExample4(){}//单例对象private static SingletonExample4 instance = null;//正常的执行步骤//1.memory = allcate() 分配对象的内存空间//2.ctorInstance()初始化对象//3.instance = memory 设置instance指向刚分配的内存// JVM 和CPU优化,发生了指令重排//1.memory = allcate() 分配对象的内存空间//3.instance = memory 设置instance指向刚分配的内存//2.ctorInstance()初始化对象//静态工厂方法public static SingletonExample4 getInstance(){if(instance == null){ //双重检测机制synchronized (SingletonExample4.class) { //同步锁if (instance == null) {instance = new SingletonExample4();}}}return instance;}
}
正常的执行步骤
1.memory = allcate() 分配对象的内存空间
2.ctorInstance()初始化对象
3.instance = memory 设置instance指向刚分配的内存上面的步骤在单线程的情况下没有问题,而在多线程情况下JVM 和CPU优化,发生了指令重排
1.memory = allcate() 分配对象的内存空间
3.instance = memory 设置instance指向刚分配的内存
2.ctorInstance()初始化对象

发生指令重排导致双重检测机制线程不安全。因此可以限制不让CPU发生指令重排。可以使用volatile关键字限制指令重排。

private static volatile SingletonExample5 instance = null;

这样就可以实现线程安全了。

因此volatile关键字有两个使用场景。1.状态标示量。2.双重检测。这里就是一个双重检测的应用。

饿汉模式

/*** 饿汉模式* 单例的实例在类装载时进行创建*/
@ThreadSafe
public class SingletonExample2 {//私有构造函数private SingletonExample2(){}//单例对象private static SingletonExample2 instance = new SingletonExample2();//静态工厂方法public static SingletonExample2 getInstance(){return instance;}
}

如果单例类的构造函数中,没有过多的操作处理,饿汉模式还可以接受。饿汉模式有什么不足呢?如果构造函数中存在过多的处理,会导致这个类在加载的过程中特别慢,因此可能存在性能问题,如果使用饿汉模式的话只进行类的加载却没有实际的调用的话,会造成资源的浪费。因此使用饿汉模式时一定要考虑两个问题,1.私有构造函数中没有太多的处理。2.这个类肯定会被使用,不会带来资源的浪费。饿汉模式属于线程安全的。

饿汉模式2

@ThreadSafe
public class SingletonExample6 {//私有构造函数private SingletonExample6(){}static {instance = new SingletonExample6();}//单例对象private static SingletonExample6 instance = null;//静态工厂方法public static SingletonExample6 getInstance(){return instance;}public static void main(String[] args) {System.out.println(getInstance());System.out.println(getInstance());}
}

这时打印出来的值为null

需要把private static SingletonExample6 instance = null;代码写在static语句块之前:

/*** 饿汉模式* 单例的实例在类装载时进行创建*/
@ThreadSafe
public class SingletonExample6 {//私有构造函数private SingletonExample6(){}//单例对象private static SingletonExample6 instance = null;static {instance = new SingletonExample6();}//静态工厂方法public static SingletonExample6 getInstance(){return instance;}public static void main(String[] args) {System.out.println(getInstance());System.out.println(getInstance());}
}

当我们在写静态属性和静态代码块时要注意顺序。

枚举模式

/*** 饿汉模式* 单例的实例在类装载时进行创建*/
@ThreadSafe
@Recommend
public class SingletonExample7 {//私有构造函数private SingletonExample7(){}public static SingletonExample7 getInstance(){return Singleton.INSTANCE.getInstance();}private enum Singleton {INSTANCE;private SingletonExample7 singleton;// JVM保证这个构造方法绝对只调用一次Singleton() {singleton = new SingletonExample7();}public SingletonExample7 getInstance() {return singleton;}}}

枚举模式相比于懒汉模式在安全性方面更容易保证,其次相比于饿汉模式可以在实际调用时才初始化,而在后续使用时也可以取到里面的值。不会造成资源浪费。

java高并发(七)发布对象相关推荐

  1. Java 高并发_JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过!...

    JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过! 1.JPG (37.82 KB, 下载次数: 0) 2018-12-3 09:40 上传 2.JPG (28 ...

  2. java高并发(一)导学

    现在准备系统学习java高并发与多线程相关知识. 首先了解一下我们这一套知识的学习思路: 并发与高并发相关概念 CPU多级缓存 缓存一致性 乱序执行优化 java内存模型 JMM规定.抽象结构 同步操 ...

  3. 从根上理解高性能、高并发(七):深入操作系统,一文读懂进程、线程、协程

    本文引用了"一文读懂什么是进程.线程.协程"一文的主要内容,感谢原作者的无私分享. 1.系列文章引言 1.1 文章目的 作为即时通讯技术的开发者来说,高性能.高并发相关的技术概念早 ...

  4. Java高并发(八)——Thread pool 如何创建及常见并行模式

    在前边 Java高并发(四)--ThreadPool,线程复用 中我们学习了ThreadPool,但是在那篇中我们在create Thread pool的时候,由于我安装了阿里的开发规范插件,一直在警 ...

  5. 《实战Java高并发程序设计》读后感

    写在前面无关的内容 白驹过隙,看下日历已经毕业4年多,加上在大学里的4年,算算在计算机界也躺了八年,按照格拉德韦尔的1万小时定律差不多我也该成为行业的专家了,然后并没有.当看着"什么是Jav ...

  6. java高并发与多线程汇总(一):基础知识(上)

    java高并发与多线程汇总 往期文章推荐:   java高并发与多线程汇总(一):基础知识(上)   java常见面试考点(四十二):序列化与反序列化   java常见面试考点(四十三):泛型   j ...

  7. SpringBoot、Redis轻松实现Java高并发秒杀系统笔记

    秒杀项目 优极限[完整项目实战]半天带你用SpringBoot.Redis轻松实现Java高并发秒杀系统 文章目录 秒杀项目 技术栈 课程介绍 学习目标 如何设计一个秒杀系统 项目搭建 分布式会话 登 ...

  8. java unsafe获取指针_【实战Java高并发程序设计 1】Java中的指针:Unsafe类

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

  9. cpu高 thread vm_阿里大佬总结,Java高并发必读!

    作者:wxdoop 原文:https://blog.csdn.net/qq_36235098 来源:前程有光 前言 进程是计算机中程序关于某几何数据集合上的一次运行活动,是系统进行资源分配和调度的基本 ...

  10. Java高并发编程:活跃性危险

    Java高并发程序中,不得不出现资源竞争以及一些其他严重的问题,比如死锁.线程饥饿.响应性问题和活锁问题.在安全性与活跃性之间通常存在依赖,我们使用加锁机制来确保线程安全,但是如果过度地使用加锁,则可 ...

最新文章

  1. 【每日DP】day1 P1802 5倍经验日(别样的01背包)难度⭐★
  2. 点云配准求物体的6D姿态(转)
  3. r语言和python-R VS Python:R语言是否真的过时了?
  4. linux 常用查看网络连接方法及脚本
  5. 宝塔nginx文件服务器,宝塔面板nginx编译webdav模块 – 建立webdav服务器
  6. celeba数据集_轻松学 Pytorch 使用DCGAN实现数据复制
  7. 一、Oracle介绍
  8. [spring]spring boot项目实例
  9. 服务器共享文件有访问台数限制,Win7/xp系统下共享文件夹最大连接数限制怎么解除...
  10. 使用loadrunner录制winsock协议的程序(原创)
  11. 可以下载MapInfo地图吗?
  12. PHP accesstoken失效,微信开发-ACCESS TOKEN 过期失效解决方案
  13. java 取上界_java-泛型及上界下界详解
  14. 误差棒到底是个什么棒?到底棒不棒!
  15. 在Spyder 中安装第三方包
  16. 共轭方式怎么判断_怎么判断共轭效应是吸电子共轭效应还是给电子共轭效应?吸电子基和给电子基是根据什么判断的?...
  17. GNU Radio教程 9.QPSK调制解调
  18. oracle spool设置字符集,spool出来的文件格式一个是UTF8一个是ASCII ??
  19. OpenGL-图元装配
  20. linux shell 关机,linux shell关机命令详解

热门文章

  1. 通用权限实现的核心设计思想
  2. 关于计算机的发展过程及基础知识正确的是,2011doc-计算机基础知识.doc
  3. Thinkphp5中异常处理不返回页面返回Json格式的字符串
  4. PHP进行生成并且导出CSV文件
  5. PHP应对洪水般的恶意访问接口 访问冲击
  6. Go的go-sql-driver/mysql
  7. 计算机应用基础模块2客观题答案 文档,计算机应用基础网上形考答案模块2 Word 2010 文字处理系统客观题答案(精).doc...
  8. html5中标签分为,HTML标签的三种类型
  9. 阶乘之和计算_利用MULTINOMIAL函数计算参数和的阶乘与各参数阶乘乘积的比 值
  10. servlet怎么接受请求_谁再问Servlet的问题,我就亲自上门来教学了