JAVA并发实战学习笔记


第三章 对象的共享

  • 失效数据

    • java程序实际运行中会出现①程序执行顺序对打乱;②数据对其它线程不可见——两种情况
    • 上述两种情况导致在缺乏同步的程序中出现失效数据这一现象,且“失效”这一情况不确定性很大,因为可能出现可能没出现。
    • JVM中没有规定对于64位变量如:long, double 的读写操作必须是原子的,因此不同步的情况下读取该类数据可能得到的值无意义(低32位和高32位没有一起形成完整的数字)

      • 商用的JVM一般会让64位变量的读取原子化。
  • 使用volatile修饰变量可以保证变量的可见性,但不会保证互斥性,是比内置锁更弱的同步机制。

    • 在以下两种情况下可以使用volatle保证同步。但是过度依赖volatile会使得代码脆弱、可读性差。

      • 1.只有一个线程对volatile变量实行写操作;
      • 2.变量不需要与其它状态变量共同参与不变约束。
    • 加锁操作兼保证原子性和可见性

  • 发布(将变量的引用保存到其它代码可以访问到的地方)与逸出(不该发布的变量被发布了):

    • 发布的几种方式:

      • public static A
      • 某个对象A的引用在另一个对象B中,发布B则间接地发布了A
      • 将对象传递给外部方法
      • 发布内部类对象A,A中隐含地包含了外部类对象B
  • 逸出

    • 在构造函数中启动了新线程(该对象的内部线程类),使得this指针在实例没有建好时便被其它线程共享了。

  • 线程封闭:线程封闭是一类编程的方法,使得各个线程中的对象是不相互共享的,如:JDBC中的Connection对象

    • Ad-hoc线程封闭:尽量少用,十分脆弱
    • 栈封闭:局部变量由于在运行中是处于线程栈的局部变量表和操作数栈中,所以局部变量无论是否是线程安全,只要其引用不发布出去,都是线程安全的。

      • 以能用局部变量就别用全局的变量,全局变量容易引起并发问题。
    • ThreadLocal类线程封闭:可将ThreadLocal t 看成是Map<Thread,T> t,保存了每个线程到T对象的一份副本,t.get()得到的是initialValue()设定的值。

      • 副本保存在线程中,随着线程的技术被垃圾回收
      • 引入了类之间的耦合性,小心使用
      • ThreadLocal类应用场景示例:移植单线程程序到多线程环境

  • 不变性

    • 满足下列三个条件的对象满足“对象不可变

      • 创建好后,不提供更改其状态的方法
      • 所有的域都是 final型
      • 对象是正确创建的
    • 对象引用不可变”————相比于对象不可变,对象的内容可以改变,但是对象地址无法改变(即用final修饰的变量)
    • 不可变对象为一系列操作提供弱原子性

      • 只要将参数传入方法,在该方法内就有指向域的引用,其它的线程修改了原对象的域,也不会影响到该方法对原域的访问。

  • 安全发布————确保可以不受JAVA不可见性的影响,得到发布的最新的对象

    • 不可变对象安全发布的方式

      • final域具有特殊的初始化安全性保证,在初始化的时候即使没有同步,也可以保证其可见性。
    • 可变对象安全发布的方式

      • 静态初始化对象(标有static的对象初始化代码是在类初始化阶段执行的,JVM自带线程安全特性)
      • 将对象的引用保存在volatile类型的域中,或者AtomicReference对象中
      • 对象引用存入final类型域中
      • 将对象引用存入某个由锁保护的域中
  • 安全访问

    • 事实不可变对象和不可变对象

      • 任意访问
    • 可变对象

      • 访问时需要同步机制,对象需要是线程安全,或者访问前先获得某个锁

第四章 对象的组合


  • 如何建立线程安全的类

    • 1.收集同步需求————候选范围为对象的域,包括对象中包含的基本类型变量,以及域对象,和域对象内的域

      • 不变性条件

        • 单个变量,即变量的值需要在其合法范围内
        • 多个变量,即多个变量的值之间需要满足某些约束
        • 访问同一个不变性条件中任何一个变量,都需要获得同一个锁,以确保对操作不会破坏不变性条件
      • 后验条件

        • 变量的值转换需要满足的约束
      • 不变性条件和后验条件约束了对象的哪些状态和状态转换是有效的
    • 2.先验条件————即依赖状态的操作

  • 实例封闭————使封装的数据被封闭在另一个对象中,被封闭的对象不超出其作用域????

    • 一般知识

      • 将对实例内封装的对象的访问限制在对象的方法上,以确保线程在访问数据是总能持有正确的锁.
      • 含有线程不安全的内部对象的线程安全类示例:

        • Collections.synchronized***()方法通过装饰器模式将容器封装在一个线程安全的对象中

      public class PersonSet{

      private final Set myset = new HashSet;

      public synchronized void addPerson(Person p){

      myset.add(p);

      }

      public synchronized boolean containsPerson(Person p){

      return myset.contains(p);

      }

      }

      • 疑惑之处:

        • 不超出作用域?如何保证方法有合适的返回值呢?觉得很奇怪。

          • 安全发布状态变量的三个条件

            • 1. 状态变量是线程安全的
            • 2. 变量不存在不变性约束
            • 3. 在方法中不包含使得该变量进入不合法状态的操作
        • 而且使用锁同步,和不使对象溢出有什么联系呢?我觉得这就是两个独立的东西,放在一起凑成一节,让人困惑

          • 猜测:要实现非线程封闭的线程安全类,封闭对象是第一步,限制访问方法并使访问方法同步是第二步。
    • Java监视器模式(即私有的所对象,而非内置锁)

      • 好处是不会让外部的方法得到该对象的锁
      • 若容器内的对象是非线程安全的,可以每次发布该对象的时候都深度复制

  • 线程安全性的委托

    • 将线程安全性委托给单个/多个(彼此独立的对象,且所有的方法中都不包含无效状态转换操作)线程安全的状态
    • 若类中包含不符合上一条要求的,包含多个有不
    • 条件约束的状态,实现线程安全性需要加锁机制

  • 在现有的线程安全类中添加新功能

    • 在并发中,能用现成的线程安全类就尽量用;若现成的类满足不了需求,则可能选择添加新功能
    • 四种方法:

      • 改源码 ————不现实
      • 扩展基础类 ———— 在自己的子类新添加的方法中使用内置锁——从这里来看,子类和父类中使用的内置锁应该是同一个,都是真对实例而言的。

        • 缺点:破坏了类的封装性
      • 客户端加锁机制 ———— 了解基础类对象使用的是什么锁,在客户端代码中使用相同的锁

        • 缺点:破坏了同步策略的封装性
      • 组合模式 ————使用装饰器模式将基础类如:list等封装在内部,将list的方法包装一层,使用装饰器类的内置锁。

        • 优点

          • 不会破坏封装性
          • 健壮性更强
          • 即使容器类不是线程安全的,也可以借此实现线程安全
        • 缺点

          • 多加一层锁,效率下降

  • 文档!!!文档!!!

    • 设计文档 & 用户文档
    • Java文档中应该注明该类使用的同步机制,应该包含下面一些内容

      • 是否是线程安全的?
      • 客户回调需不需要加锁,可以加那些锁?
      • 哪些锁保护了哪些状态?(设计文档,可以用java注释便于后续开发)

原文:https://www.cnblogs.com/tanyalew/p/9340830.html

java后验条件_JAVA并发实战学习笔记——3,4章~相关推荐

  1. 网易云课堂微专业--Java高级开发工程师--多线程并发编程--学习笔记(二)

    文章目录 第一章 多线程并发编程 第二节 线程安全问题 1.2.1 线程安全之可见性问题 多线程中的问题 从内存结构到内存模型 工作内存缓存 指令重排序 内存模型的含义 Shared Variable ...

  2. java中解决脏读_java并发编程学习之脏读代码示例及处理

    使用interrupt()中断线程     当一个线程运行时,另一个线程可以调用对应的Thread对象的interrupt()方法来中断它,该方法只是在目标线程中设置一个标志,表示它已经被中断,并立即 ...

  3. java中线程总结_java中多线程学习笔记总结

    线程的简单学习笔记: 1.进程与线程的概念 进程:从用户角度看进程是应用程序的一个执行过程. 从操作系统核心角度看进程代表的是操作系统分配的内存和CPU时间片等资源的基本单位,是为正在运行的程序提供的 ...

  4. Java秒杀系统方案优化 高性能高并发实战 学习笔记

    秒杀系统 (一)搭建环境 自定义封装Result类 自定义封装CodeMsg类 集成redis和rabbit 封装RedisService类 断言和日志测试 (二)实现用户登录和分布式Session ...

  5. java 对象 线程安全_JAVA并发编程学习:构造线程安全的对象

    设计线程安全的类 实例限制 当一个对象被另一个对象封装时,所有访问被被封装对象的代码路径就是全部可知的,这相比于让对象可被整个系统访问来说,更容易对代码路径进行分析.将数据封装在对象内部,把对数据的访 ...

  6. java公社博客_Java面向对象开发学习笔记(二)

    课时4 类与对象(对象内存分析) 如果要想进行对象的产生分析,首先就必须清楚引用类型.引用类型指的是内存空间的操作.而对于现在的内存,我们主要使用两块内存空间: 堆内存空间:保存真正的数据,堆内存保存 ...

  7. java公社博客_Java面向对象开发学习笔记(一)

    Java面向对象开发 共105课时 课时1 面向对象简介 面向对象是一种程序设计方法,但是并不是所有开发者都认同面向对象,因为很多开发者认为面向对象过于复杂,所以更多人愿意使用函数式编程. 面向对象的 ...

  8. Java 8 实战学习笔记

    Java 8 实战学习笔记 @(JAVASE)[java8, 实战, lambda] 文章目录 Java 8 实战学习笔记 参考内容 Lambda表达式 Lambda环绕执行模式(抽离步骤) 原始代码 ...

  9. 先验条件、后验条件、不变条件

    You'll have a lot of problems writing Java, especially multi-threaded code, if you can't understand ...

最新文章

  1. 相似图片检测:感知哈希算法之aHash,dHash,pHash的Python实现
  2. 【Java】5.5 深入构造器
  3. oracle触发器的类型及使用方法
  4. php ci nginx 伪静态rewrite配置方法
  5. 关于.cpp文件包含另一个.cpp文件出错的原因以及解决办法
  6. Flask常见问题记录
  7. 使用Lingo做灵敏度分析
  8. GB2312、GBK、UTF-8 如何转换
  9. xshell如何设置选中复制_Xshell
  10. 贪吃蛇php代码下载,C语言贪吃蛇代码
  11. 主编编辑器怎么把文章转成长图?
  12. ssl checker
  13. Unity3D关于iTween知识详解和接口总结
  14. Python面向对象加强4.iter与next的用法,枚举器enumerate
  15. 阅读笔记:利用Python进行数据分析第2版——第10章 数据聚合与分组运算
  16. 大学计算机实验报告u盘启动安装,用U盘安装Win7操作系统实验报告
  17. 网络字节序与主机字节序的转换 - HEN_MAN的专栏 - 博客频道 - CSDN.NET
  18. Mac 开启 HIDPI 设置各种分辨率 HIDPI mac high sierra 2k
  19. 微信会员卡,信息类目字段跳转小程序【custom_field1】【巨巨巨巨坑】
  20. Dev GridView网格中格式化 TimeSpan

热门文章

  1. ARM 学习知识点梳理
  2. 写技术博客,如何选择博客平台
  3. 祝贺深圳华南销售部成功签订广州某会议室室内P2.5全彩LED显示屏项目
  4. 摄像头基础介绍【转】
  5. 护士成绩用计算机改卷,解密!2020年卫生资格/护士人机对话考试如何评分?成绩如何核算?...
  6. 麻将服务端架设linux,湖南房卡麻将客户端/服务器端完整源码及编译教程
  7. 机器学习之推荐系统的基础知识
  8. 人一辈子必看的超级经典人生语录
  9. 生命在于学习——docker逃逸
  10. Python零基础速成班-第2讲-Python基础(上),运算、变量、数据类型、输入输出