首页 > 学院 > 开发设计 > 正文

第2章-对象的共享

2019-11-09 19:30:51
字体:
来源:转载
供稿:网友

1.分析清单3-1 2.syn是否禁止指令重排序 3.volatile修饰引用类型 4.java中的不变性条件指什么

要编写正确的并发程序,关键问题在于:在访问共享的可变状态时进行正确的管理。

第二章介绍了如何通过避免多个线程在同一时刻访问相同的数据,而本章将介绍如何共享和发布对象,从而使他们能够安全地由多个线程同时访问。

synchronized 1.原子性 2.内存可见性(memory visibility)3.互斥性

在没有同步的情况下,编译器处理器以及运行时等都可能对操作的执行顺序进行一些意想不到的调整。在缺乏足够同步多线程程序中,要想对内存操作的执行顺序进行判断,几乎无法得出正确结论。

在缺乏同步的程序中,可能会出现一种产生错误结果的情况:失效数据

出现失效数据的可能 :

不能保证原子性不能保证可见性存在指令重排序

失效数据可能会导致一些严重的安全问题或者活跃性问题,比如

输出错误的值使程序无法结束,无限循环意料之外的异常被破坏的数据结构不精确的计算

当线程在没有同步的情况下读取变量时,可能会得到一个失效值,但至少这个值是由之前某个线程设置的值,而不是一个随机值,这种安全性保证也被称为最低安全性(out-of-thin-airsafety)

最低安全性适用于绝大多数变量,但是存在一个例外:非volatile类型的64位数值变量(double和long)。java内存模型要求,变量的读取操作和写入操作都必须是原子操作,但对于非volatile类型的long和double变量,jvm允许将64位的读操作或写操作分解为两个32位的操作。当读取一个非volatile类型的long变量时,如果对该变量的读操作和写操作在不同的线程中执行,那么很可能会读取到某个值的高32位和另一个值的低32位。因此,即使不考虑失效数据,在多线程程序中使用共享且可变的long和double等类型变量也是不安全的,(这里是指既保证不了最低安全性,也保证不了原子性 。。。)除非

用关键字volatile来声明它们用锁保护起来

内置锁可以用于确保某个线程以一种可预测的方式来查看另一个线程的执行情况

java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,(因此不能将局部变量声明为volatile)因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型变量时总会返回最新的值

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

在当前大多数处理器架构上,读取volatile变量的开销只比读取非volatile变量的开销略高一些。

volatile变量的正确使用方式包括:

确保他们自身状态的可见性确保他们所引用对象状态的可见性标识一些重要的程序生命周期事件的发生(例如,初始化或关闭)

volatile变量通常用作某个操作完成、发生中断或者状态的标志

volatile的语义不足以确保递增操作(count++)的原子性,除非你能确保只有一个线程对变量执行 操作

当且仅当满足以下所有条件时,才应该使用volatile变量:

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

发布(Publish)一个对象的意思是指,使对象能够在当前作用域之外的代码中使用。比如:

将一个指向该对象的引用保存到其他代码可以访问的地方(公有静态变量)在某一个私有的方法中返回改引用将引用传递到其他类的方法中(方法传递,即alien方法,发布一个内部类的实例)

在许多情况中,我们要确保对象及其内部状态不被发布。而在某些情况下,我们又需要发布某个对象,但如果发布时要确保线程安全性,则可能需要同步。发布内部状态可能会破坏封装行,并且使程序难以维持不变性条件。如果在对象构造完成之前就发布该对象,就会破坏线程安全性

当某个不应该发布的对象被发布时,这种情况就被称为逸出(Escape)。

当发布某个对象时,可能会间接地发布其他对象。

当发布一个对象时,在该对象的非私有域中引用的所有对象同样会被发布。

假定有一个类C,对于C来说,“外部方法(alien)” 是指行为并不完全由C来规定的方法,包括

其他类中定义的方法类C中可以被改写的方法(既不是私有方法也不是final方法)

当把一个对象传递给某个外部方法时,就相当于发布了这个对象。

发布内部类实例时,也隐含地发布了外部类实例本身,因为在这个内部类的实例中包含了外部类实例的隐含引用。

不安全(正确)的对象构造过程有可能导致this逸出,比如

在构造函数中发布一个匿名内部类在构造函数中启动一个线程在构造函数中调用一个可改写的实例方法(alien方法)
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表