博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[Java并发编程实战] 第3章 对象的共享
阅读量:2342 次
发布时间:2019-05-10

本文共 1833 字,大约阅读时间需要 6 分钟。

可见性:

  • 缺乏同步可能产生:失效数据

  • 最低安全性:当线程在没有同步的情况下读取变量时,可能得到一个失效值,但至少是之前某个线程设置的值,而不是一个随机值

  • 最低安全性的例外:非volatile类型的64位数值变量,所以多线程共享可变的long和double等类型的变量是不安全的,除非用volatile声明,或用锁保护起来

  • 加锁的含义不仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作或写操作的线程都必须在同一个锁上同步

  • 访问volatile变量不会执行加锁操作,也就不会是执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制

  • 加锁可以确保可见性与原子性,volatile只能确保可见性

  • 当且仅当满足以下所有条件才应该用volatile:

    • 对变量的写入不依赖变量当前值,或者能保证只有单个线程更新变量的值
    • 该变量不会与其他状态变量一起被纳入不变性条件
    • 在访问变量时不需要加锁

发布与逸出

  • 发布对象:使对象能在当前作用域之外的代码中使用

  • 发布对象的方法:

    • 将对象引用保存到一个公有的静态变量中
    • 发布某个对象可能会间接发布其他对象
    • 从非私有方法中返回一个引用
    • 发布一个对象,该对象的非私有域中引用的所有对象都会被发布
    • 发布一个内部的类实例
  • 外部方法:对类C来说,外部方法指行为不完全由C来规定的方法,包括其它类中定义的方法和C中可以被改写的方法(不是private或final的方法)

  • this引用逸出的几种情况:

    • 在构造函数中引入了一个内部类EventListener,而内部类会自动的持有其外部类(这里是ThisEscape)this引用。source.registerListener会将内部类发布出去,从而ThisEscape.this引用也随着内部类被发布了出去。

    • 构造函数中启动一个线程,this引用会被新创建的线程共享

    • 在构造函数中调用一个可改写的实例方法(既不是私有方法,又不是final方法)

线程封闭

  • Ad-hoc线程封闭:维护线程封闭性的职责完全由程序实现来承担(很脆弱,尽量少用)

  • 栈封闭:只能通过局部变量访问对象,多个线程访问一个方法,此方法中的局部变量都会被拷贝一份到线程栈中。所以局部变量是不被多个线程所共享的,也就不会出现并发问题

  • ThreadLocal类:使线程中某个值与保存值的对象关联起来,为每个使用该变量的线程存有一份独立的副本,防止对可变的单实例变量或全局变量进行共享

不变性

  • 满足以下条件的对象才是不可变的:

    • 对象创建后其状态就不能修改
    • 对象所有域都是final的
    • 对象是正确创建的
  • 不可变对象内部仍然可以使用可变对象来管理他们的状态

  • 不可变对象vs不可变的对象引用:保存在不可变对象中的程序状态仍然可以更新,即通过一个保存新状态的实例来替换原有的不可变对象

  • 一个正确构造的对象可以通过以下方式安全发布:

    • 在静态初始化函数中初始化一个对象引用(静态初始化器由JVM在类的初始化阶段执行,由于JVM内部存在着同步机制,因此通过这种方法初始化的任何对象都可以被安全发布
    • 将对象的引用保存到volatile类型的域或AtomicReference对象中
    • 将对象引用保存到某个正确构造对象的final类型域中
    • 将对象引用保存到一个由锁保护的域中
  • 线程安全容器的安全发布保证:

    • 将一个键或者值放入Hashtable/synchronizedMap/ConcurrentMap中,可以安全地将它发布给任何从这些容器中访问它的线程
    • 将某个元素放入Vector/CopyOnWriteArrayList/CopyOnWriteArraySet/synchronizedList/sunchronizedSet中,可以将元素安全发布到任何从这些容器中访问该元素的线程
    • 将某个元素放入BlockingQueue/ConcurrentLinkedQueue中,可以将元素安全发布到任何从这些队列中访问该元素的线程
  • 事实不可变对象:对象从技术上看是可变的,但其状态在发布后不会改变

  • 对象的发布需求取决于它的可变性:

    • 不可变对象可以通过任意机制发布
    • 事实不可变对象通过安全方式发布
    • 可变对象通过安全方式发布,并且必须是线程安全的或者由某个锁保护起来
  • 并发程序中使用和共享对象,实用策略:

    • 线程封闭
    • 只读共享:不可变/事实不可变对象
    • 线程安全共享:内部同步
    • 保护对象:锁

转载地址:http://qlkvb.baihongyu.com/

你可能感兴趣的文章
linux shell 自定义函数(定义、返回值、变量作用域)介绍
查看>>
写自己的ASP.NET MVC框架(上)
查看>>
C++和C在linux下编程和与在WINDOWS下有什么区别
查看>>
CSS 的优先级机制[总结]
查看>>
linux shell 数组建立及使用技巧
查看>>
IEnumerator 协程 全称协同程序
查看>>
java实现冒泡排序
查看>>
spring boot 初试,springboot入门,springboot helloworld例子
查看>>
Spring中配置和读取多个Properties文件--转
查看>>
使用JNI进行Java与C/C++语言混合编程(1)--在Java中调用C/C++本地库
查看>>
Mac 终端命令连接mysql
查看>>
Lua中的数学库
查看>>
多态小结
查看>>
Java连MySQL的驱动mysql-connector-java-5.1.21-bin.jar的安装方法
查看>>
java基础小结
查看>>
线程概念及死锁的理解
查看>>
数据结构之红黑树
查看>>
android学习之——界面 控件 体系 布局
查看>>
Eclipse开发Android程序在手机上运行
查看>>
ListView深入理解
查看>>