首页 > 代码库 > 对象的共享

对象的共享

1.可见性:我们不仅希望防止某个线程正在使用对象状态而另一个线程在同时修改该状态,而且希望确保当一个线程修改了对象状态后,其他线程能够看到发生的状态 变化。

 技术分享

 

NoVisibility可能会持续循环下去,因为读线程可能永远都看不到ready的值。还有一种情况下,可能会输出0,他看到了ready,却没有看到number,这种情况叫做“重排序”

也就是说,在没有同步的情况下,编译器、处理器以及运行时都可能对操作的执行顺序进行一些意想不到的调整。

 

1.1    失效数据

NoVisibility展现了一种可能产生错误结果的一种情况:失效数据。

 技术分享

 

这个类不是现成安全的,get和set都是在没有同步的情况下访问value的。如果某个线程调用了set,那么另一个正在调用get的线程可能会看到set以后的数据,也可能看不到

 技术分享

 

通过使用同步方法,就成了一个线程安全的类

1.2    非原子的64位操作

线程在没有同步的情况下,读取的一个失效值,再怎么也是之前某个线程设置的,而不是随机的,这叫做最低安全性,不过有一个例外,非volatile类型的64位数值变量(double和long).JVM允许将64的读操作或写操作分解为两个32位的操作。如果读取一个非volatile类型的long变量,读和写在不同的线程内执行,很可能得到一个值得高32位和另一个值得低32位。因此,在多线程程序中使用共享且可变的long和double等类型的变量也是不安全的!除非用volatile来声明他们,或者用锁保护起来

1.3    加锁与可见性

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

1.4    Volatile变量

一种稍弱的同步机制,用来确保将变量的更新通知到其他线程。

当把一个变量声明为volatile类型之后,编译器和运行时都会注意到这个变量时共享的,因此不会将该变量上的操作与其他内存操作一起重排序,因此在读取volatile类型的变量时总会返回最新写入的值。

仅当volatile变量能简化代码的实现以及对同步策略的验证时,才应该使用它们。Volatile变量的正确使用方式是:确保它们自身状态的可见性,确保他们所引用对象的状态的可见性,以及表示一些重要的程序生命周期事件的发生。

 

加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性。

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

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

2.发布和逸出

“发布”一个对象指的是使对象能够在当前作用域之外的代码中使用。当某个不该发布的对象被发布时,这种情况就叫做“逸出”

将一个指向该对象的引用保存到其他代码可以访问的地方

技术分享

 

发布对象的最简单方法就是将对象的引用保存到一个公有的静态变量中。在initialize方法中实例一个新的HashSet对象,并发布。当发布某个变量时,可能会间接的发布其他对象如果将一个Sectet对象添加到knownSecrets中,那么同样会发布这个对象。

 

在一个非私有的方法中返回该引用

 技术分享

 

如果按照上述方式来发布states,就会使内部的可变状态逸出。任何调用者都可以改变这个数组的内容

 

将引用传递到其他类的方法中

 技术分享

 

发布一个内部的类实例,当发布EventListener()时,也隐含的发布了ThisEscape实例本身。

 

安全的对象构造过程

上面从构造函数中发布对象时,只是发布了一个尚未构造完成的对象。即使发布对象位于构造函数的最后一行也是如此。因此不要在构造过程中使this引用逸出。

 技术分享

 

如果想在构造函数中注册一个事件监听器或者启动线程,那么可以使用一个私有的构造函数和一个公共的工厂方法。

3 线程封闭

为了避免使用同步的方式就是不共享数据,如果仅在单线程内访问数据,就不需要同步,这种技术叫做线程封闭

当某个被封闭的对象本身不是线程安全的封闭在一个线程中时,也将自动实现线程安全性

3.1  Ad—hoc线程封闭

指的是:维护线程封闭性的职责完全由程序来实现承担!

在volatile中存在一种特殊的线程封闭。只要能确保只有单个线程对共享的volatile变量执行写入操作,那么就可以安全的在这些共享的volatile变量上执行“读取-修改-写入”操作。在这种情况下,相当于将修改操作封闭在当个线程中以防止发生竞态条件,而volatile的可见性还保证了其他线程可以看到最新的值

3.2栈封闭

一种特例。在栈封闭中,只能通过局部变量才能访问对象。局部变量的固有属性之一就是封闭在执行线程中。它们位于执行线程的栈中,其他线程无法访问这个栈。栈封闭也被称为线程内部使用或者线程局部使用

3.3 ThreadLocal类

 这个类能使线程中的某个值与保存值得对象关联起来。ThreadLocal提供了get与set等访问接口或方法

这个类为提供了get和set方法,这些方法为每个使用该变量的线程都存有一份独立的副本,因此get总是返回由当前执行线程再调用set时设置的最新值。

ThreadLocal对象通常用于防止对可变的单实例变量或全局变量进行共享

 

对象的共享