《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

时间:2023-02-04 09:26:27

这章的主要内容是:如何共享和发布对象,从而使它们能够安全地由多个线程同时访问。

内存的可见性

确保当一个线程修改了对象状态后,其他线程能够看到发生的状态变化。

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

上面的程序中NoVisibility可能会持续循环下去,因为读线程可能永远都看不到ready的值。一种更奇怪的现象是NoVisibility可能会输出0,因为读线程可能看到了写入ready的值,但却没有看到之后写入number的值,这种现象被称为“重排序”。多线程之指令重排序

失效数据

简而言之就是在缺乏同步的程序中可能会读取到过期的数据,也就是失效数据,就像上面的例子一样,当度线程查看ready变量时可能会的得到一个失效的值。

非原子的64位操作

虽然得到的可能是一个失效值,但至少这个值是由之前每个线程设置的,而不是一个随机值。这种安全性保证也被称为最低安全性。最低安全性适用于绝大多数变量,但是对于非volatile类型的64位数值变量(double和long)并非如此。java变量的读操作和写操作都是原子的,但是JVM允许将64位的读操作和写操作分解为2个32为的操作。这样当读取一个非volatile的long变量时,如果对该变量的读操作和写操作在不同的线程中执行,那么很可能会读取到某个值的高32为和另一个值的低32位。

加锁与可见性

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

Volatile变量

确保将变量的更新操作通知到其他线程,当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。在读取volatile类型的变量时总会返回最新的写入值。下面的程序给出了volatile变量的一种典型用法:检查每个状态标记以判断是否退出循环。

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

volatile的语义不足以确保递增操作(count++)的原子性。当且仅当满足一下所有条件是,才应该使用volatile变量:

1. 对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值

2. 该变量不会与其他状态变量一起纳入不变性条件中

3. 在访问变量时不需要加锁

Java中Volatile关键字详解&&Java并发编程:volatile关键字解析

发布与逸出

“发布”一个对象是指:使对象能够在当前作用域之外的代码中使用。当某个不该发布的对象被发布时,这种情况就被称为逸出。发布对象的最简单方法是将对象的引用保存到一个公有的静态变量中;

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

安全的对象构造过程

不要在构造过程中使this引用逸出。在构造过程中使this引用逸出的一个常见的错误是:在构造函数中启动一个线程。当对象在其构造函数中创建一个线程时,无论是显示创建还是隐士创建,this引用都会被新创建的线程共享。(简而言之在对象的构造函数中的别的对象能够拿到当前对象的this引用从而造成逸出)。

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

线程封闭

不共享数据,仅在单线程内访问数据,避免使用同步的方式。这种技术被大量使用喻Swing和JDBC的Connection对象。

Ad-hoc线程封闭

指维护线程封闭性的职责完全由程序实现来承担。很脆弱,尽量使用更强的线程封闭技术(如栈封闭或ThreadLocal类)

栈封闭

只能通过局部变量才能访问对象。局部变量的固有属性之一就是封闭在执行线程中。他们位于执行线程的栈中,其他线程无法访问这个栈。

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

ThreadLocal类

这个类能使线程中的某个值与保存值的对象关联起来。ThisLocal提供了get和set等访问接口或方法,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。ThreadLocal对象通常用于防止对可变的单实例或全局变量进行共享。通过将JDBC的连接保存到ThreadLocal对象中(因为JDBC的连接对象不一定是线程安全的),每个线程都会有属于自己的连接。

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

当某个频繁执行的操作需要一个临时对象,例如缓冲区,同时又希望避免在每次执行时都重新分配该临时对象,就可以使用这项技术。当某个线程初次调用ThreadLocal.get方法时,救会调用initialValue来获取初始值。

不变性

满足同步需求的另一种方法是使用不可变对象,即对象创建以后状态不能修改,它的所有域都是final类型,并且对象是正确创建的(this引用没有逸出)。在不可变对象的内部仍可以使用可变对象来管理它们的状态,如

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

Final域

final类型的域是不能修改的,但如果final域所引用的对象是可变的(如上例),那么这些被引用的对象是可以修改的。

示例:使用volatile类型来发布不可变对象

继续前面因式分解的例子。因式分解Servlet将执行两个原子操作:更新缓存的结果,以及通过判断缓存中的数值是否等于请求的数值来决定是否直接读取缓存中的因数分解结果。每当需要对一组相关数据以原子方式执行某个操作时,就可以创建一个不可变的类来包含这些数据,如:

对数值及其因数分解结果进行缓存的不可变容器类

@Immutable
class OneValueCache {
private final BigInteger lastNumber;
private final BigInteger[] lastFactors;
/**
* 如果在构造函数中没有使用 Arrays.copyOf()方法,那么域内不可变对象 lastFactors却能被域外代码改变
* 那么 OneValueCache 就不是不可变的。
*/
public OneValueCache(BigInteger i,
BigInteger[] factors) {
lastNumber = i;
lastFactors = Arrays.copyOf(factors, factors.length);
}
public BigInteger[] getFactors(BigInteger i) {
if (lastNumber == null || !lastNumber.equals(i))
return null;
else
return Arrays.copyOf(lastFactors, lastFactors.length);
}
}

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

安全发布

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

不正确的发布,正确的对象被破坏

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

不可变对象与初始化安全性

任何线程都可以子在不需要额外同步的情况下安全地访问不可变对象(状态不可变,域都是final类型,正确的构造过程)

安全发布的常用模式

可变对象必须通过安全的方式来发布。

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

事实不可变对象

本身可变但对象在发布后不会被修改。在没有额外的同步情况下,任何线程都可以安全地使用被安全发布的事实不可变对象。

可变对象

不仅需要安全发布,而且需要额外的同步和线程安全来保护

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

安全地共享对象

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性的更多相关文章

  1. Java并发编程实战 读书笔记(一)

    最近在看多线程经典书籍Java并发变成实战,很多概念有疑惑,虽然工作中很少用到多线程,但觉得还是自己太弱了.加油.记一些随笔.下面简单介绍一下线程. 一  线程与进程   进程与线程的解释   个人觉 ...

  2. 读书笔记-----Java并发编程实战(二)对象的共享

    public class NoVisibility{ private static boolean ready; private static int number; private static c ...

  3. Java并发编程实战 读书笔记(二)

    关于发布和逸出 并发编程实践中,this引用逃逸("this"escape)是指对象还没有构造完成,它的this引用就被发布出去了.这是危及到线程安全的,因为其他线程有可能通过这个 ...

  4. Java并发编程实战 第3章 对象的共享

    可见性 可见性是由于java对于多线程处理的内存模型导致的.这似乎是一种失败的设计,但是JVM却能充分的利用多核处理器的强大性能,例如在缺乏同步的情况下,Java内存模型允许编译器对操作顺序进行重排序 ...

  5. 【JAVA并发编程实战】1、对象的共享

    1.栈封闭 在栈封闭中,只能通过局部变量才能访问对象. 所谓栈封闭就是把变量的声明以及应用都局限在一个局部线程中,在这个局部线程中声明和实例化的对象对于线程外部是不可见的,这个局部线程的栈,无法被任何 ...

  6. 《java并发编程实战》笔记

    <java并发编程实战>这本书配合并发编程网中的并发系列文章一起看,效果会好很多. 并发系列的文章链接为:  Java并发性和多线程介绍目录 建议: <java并发编程实战>第 ...

  7. Java多线程编程实战读书笔记(一)

    多线程的基础概念本人在学习多线程的时候发现一本书——java多线程编程实战指南.整理了一下书中的概念制作成了思维导图的形式.按照书中的章节整理,并添加一些个人的理解.

  8. Java并发编程实践读书笔记(1)线程安全性和对象的共享

    2.线程的安全性 2.1什么是线程安全 在多个线程访问的时候,程序还能"正确",那就是线程安全的. 无状态(可以理解为没有字段的类)的对象一定是线程安全的. 2.2 原子性 典型的 ...

  9. 【JAVA并发编程实战】2、对象的组合

    1. 设计线程安全的类 1.找出构成对象状态的所有变量 2.找出约束状态变量的不变性条件 3.建立对象状态的并发访问管理策略 package cn.xf.cp.ch04; /** * *功能:JAVA ...

  10. Java并发编程艺术读书笔记

    1.多线程在CPU切换过程中,由于需要保存线程之前状态和加载新线程状态,成为上下文切换,上下文切换会造成消耗系统内存.所以,可合理控制线程数量. 如何控制: (1)使用ps -ef|grep appn ...

随机推荐

  1. 如何向非技术人(程序猿)解释SQL注入?

    前两天看博客园新闻,有一篇文章名为<我该如何向非技术人解释SQL注入?>(http://kb.cnblogs.com/page/515151/).是一个外国人写的,伯乐在线翻译的.我当时看 ...

  2. Swift安装

    Server1 .Update sudo apt-get update sudo apt-get upgrade . sudo apt-get install bridge-utils .IP 3.1 ...

  3. NOIP2014pj子矩阵&lbrack;搜索|DP&rsqb;

    题目描述 给出如下定义: 子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序)被称为原矩阵的一个子矩阵. 例如,下面左图中选取第2.4行和第2.4.5列交叉位置的元素 ...

  4. &lbrack;转载&rsqb;Jquery mobile 新手问题总汇

    原文链接:http://www.wglong.com/main/artical!details?id=4 此文章将会持续更新,主要收录一些新手比较常见的问题. 欢迎 向我推荐比较典型的常见问题,我会记 ...

  5. 关于jQuery的bind&lpar;&rpar;&bsol;trigger&lpar;&rpar;&bsol;triggerHandler&lpar;&rpar;

    1.bind() 事件绑定. 多个事件会链式累加,而不会覆盖. 即 $("div").bind("click",funtion(){alert("te ...

  6. POJ 3468 线段树裸题

    这些天一直在看线段树,因为临近期末,所以看得断断续续,弄得有些知识点没能理解得很透切,但我也知道不能钻牛角尖,所以配合着刷题来加深理解. 然后,这是线段树裸题,而且是最简单的区间增加与查询,我参考了A ...

  7. Openjudge-计算概论(A)-点与正方形的关系

    描述: 有一个正方形,四个角的坐标(x,y)分别是(1,-1),(1,1),(-1,-1),(-1,1),x是横轴,y是纵轴.写一个程序,判断一个给定的点是否在这个正方形内.输入输入坐标x,y输出ye ...

  8. bzoj2054疯狂的馒头——线段树

    中文题面,一排有n个馒头,用刷子把整个连续的区间刷成一种颜色.因为颜色会覆盖掉之前的.所以我们可以用线段树来反着处理.如果这段区间之前刷到过就不要再遍历进去了,因为这次已经被上次刷的颜色给覆盖了.最后 ...

  9. python模块之logging模块

    1. 低配版 # 指定显示信息格式 import logging logging.basicConfig( level=20, # 设置显示或写入的起始级别 format="%(asctim ...

  10. Python3学习笔记04-运算符

    算术运算符 +     加两个对象相加 -      减得到负数或是一个数减去另一个数 *     乘两个数相乘或是返回一个被重复若干次的字符串 /     除x 除以 y %    取模返回除法的余 ...