Java高并发--安全发布对象
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();这句其实是分三步完成的。
分配对象的内存空间
初始化对象
设置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高并发--安全发布对象的更多相关文章
- java高并发系列 - 第13天:JUC中的Condition对象
本文目标: synchronized中实现线程等待和唤醒 Condition简介及常用方法介绍及相关示例 使用Condition实现生产者消费者 使用Condition实现同步阻塞队列 Object对 ...
- Java高并发--线程安全策略
Java高并发--线程安全策略 主要是学习慕课网实战视频<Java并发编程入门与高并发面试>的笔记 不可变对象 发布不可变对象可保证线程安全. 实现不可变对象有哪些要注意的地方?比如JDK ...
- Java高并发综合
这篇文章是研一刚入学时写的,今天整理草稿时才被我挖出来.当时混混沌沌的面试,记下来了一些并发的面试问题,很多还没有回答.到现在也学习了不少并发的知识,回过头来看这些问题和当时整理的答案,漏洞百出又十分 ...
- [ 高并发]Java高并发编程系列第二篇--线程同步
高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...
- 【实战Java高并发程序设计 7】让线程之间互相帮助--SynchronousQueue的实现
[实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...
- 【实战Java高并发程序设计 5】让普通变量也享受原子操作
[实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...
- 【实战Java高并发程序设计 4】数组也能无锁:AtomicIntegerArray
除了提供基本数据类型外,JDK还为我们准备了数组等复合结构.当前可用的原子数组有:AtomicIntegerArray.AtomicLongArray和AtomicReferenceArray,分别表 ...
- 【实战Java高并发程序设计 3】带有时间戳的对象引用:AtomicStampedReference
[实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference AtomicReference无法解决上述问题的根 ...
- 【实战Java高并发程序设计 1】Java中的指针:Unsafe类
是<实战Java高并发程序设计>第4章的几点. 如果你对技术有着不折不挠的追求,应该还会特别在意incrementAndGet() 方法中compareAndSet()的实现.现在,就让我 ...
随机推荐
- 简单操作django中session和cookie
cookie 1.会话技术 2.客户端的会话技术( 数据库保存在浏览器上) 3.问题导致原因: 在web应用中,一次网络请求是从request开始,到response结束,跟以后的请求或者跟其他请求没 ...
- iOS 数组问题
在iOS开发过程中,使用json抓取网络数据进行解析时,用tableview承载,发现数据已经抓取到,但是在cell里面使用却会导致程序崩溃 原因可能是初始化方法问题,将 _infoArray=[[N ...
- [翻译] Visual Studio 2019: 极速编码. 智能工作. 创造未来.
原文: Visual Studio 2019: Code faster. Work smarter. Create the future. Visual Studio 2019 的正式版现在可以下载了 ...
- Android插件化的兼容性(下):突破Android P中灰黑名单的限制
在Android P系统中,加入了访问私有API接口的限制.
- wordpress背景添加跟随鼠标动态线条特效
今天看到别人的博客,在鼠标移动背景时会出现线条动态特效 感觉挺有意思的,还有另一种,在背景点击时会跳出字符特意去找了方法,以为需要添加代码的,结果只要安装个插件就可以了,所以说wordpress就是方 ...
- Go语言strings和strconv包
目录 前缀和后缀 字符串包含关系 判断子字符串或字符在父字符串中出现的位置(索引) 字符串替换 统计字符串出现次数 重复字符串 修改字符串大小写 修剪字符串 分割字符串 拼接slice到字符串 从字符 ...
- JDK设计模式之——工厂模式
1.首先来看最普通的工厂模式 1.1 定义一个需要工厂生产的java类 package javaee.net.cn.factory; class Person{ private int age; pr ...
- Python学习笔记【第三篇】:if判断、while循环、for循环
如果某些条件满足,才能做某件事情,而不满足时不允许做,这就是所谓的判断 不仅生活中有,在软件开发中“判断”功能也经常会用到 if判断语句 if 要判断的条件: 条件成立 num = 50 if num ...
- Spark streaming消费Kafka的正确姿势
前言 在游戏项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端,我们利用了spark streaming从kafka中不 ...
- eos开发(二)使用cleos命令行客户端操作EOS(钱包wallet基础操作)
不知道下边这一段英文你们是不是能看懂,如果看不懂那就算了,我就是转过来随便看看的. 总之你记住nodeos.cleos和keosd这三个工程十分重要就行了,回头咱们的研究都从这三个工程杀进去. EOS ...