java并发编程实战笔记---(第三章)对象的共享

3.1 可见性

synchronized 不仅实现了原子性操作或者确定了临界区,而且确保内存可见性

*****必须在同步中才能保证:当一个线程修改了对象状态之后,另一个线程可以看到发生的状态变化。

1.失效值问题

 

 

以上类非线程安全,get和set在非同步情况下获取value值。

当一个线程修改value,另一个线程可能得到更新后的值,也可能得不到。

 

 

对get和set进行同步,可以是成为线程安全类。

 

2. long或者double 需要用volatile修饰或者用锁保护。 因为64位值可能被拆为2个32位操作。

 

3.volatile 修饰变量,确保获取最新值。

加锁可以确保可见性和原子性,而volatile只能确保可见性。

 

 

可以被写入 volatile 变量的这些有效值独立任何程序的状态包括变量的当前状态:

  • 对变量的写操作不依赖于当前值。
  • 该变量没有包含在具有其他变量的不变式中。

lower upper非独立于其他状态

3.2发布与逸出

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

 

逸出: 某个不应该被发布的对象被发布。

 

 

 

 

 

 3.3线程封闭

 

 将某个对象封闭在某个线程中。举例:connection从线程池拿出给某个线程使用,且不会在分给其他线程。

java及核心库提供支持:例如 局部变量和ThreadLocal

但是程序员仍要确保封闭在线程中的对象不会从线程中逸出。

 

2栈封闭

局部变量的固有属性,就是封闭在执行的栈中。其他线程无法访问这个栈。但是编程要确保不逸出。

 

 

 开发人员要给出注释那些对象需要封闭到执行线程中,便于以后维护时不会造成逸出。

 

3.threadlocal

 

3.4不变性

不可变对象一定是线程安全的。

 

满足以下条件时,对象才不可变:

1.对象创建以后其状态就不能修改。

2.对象的所有域都是final类型。

3.对象是正确创建的(对象的创建期间,this引用没有逸出)。

 

1.Final域

 

2.volatile类型来发布不可变对象(volatile+不可变对象)

当需要对一组数据以原子方式执行某些操作时,可以考虑创建一个不可变类来封装这些数据。如上例。

对于在访问和更新多个相关变量时出现的竞争性条件,通过将这些变量保存在一个不可变类中来消除。不可变对象,当线程获得该变量引用后,不必担心其他线程会修改该对象的状态。如果要更新该对象,可以创建    一个新的容器对象   。其他线程没有影响。

总结:将变化的封装在一个不变的容器类中,讲内容的变化改为创建一个新的容器。

 

 

 

 

 

 3.5安全发布

上面讨论的是如何让对象不被发布,讲对象封闭在线程或者另一个对象中。但是有时需要在线程中共享对象,所以需要安全发布对象。

//在没有足够同步的情况下发布对象,不要这么做

public Holder holder;

public void intialize() {

  holder =  new Holder(42);

}

存在可见性问题,其他线程看到未被完全创建的对象。

 

1.不正确的发布:正确的对象被破坏

 

class Holder{

  private int n;

  public Holder(int n) {  this.n = n; }

  public void assertSanity() {

    if( n != n) {

      throw new AssertionError("this statement is error");

    }

  }

}

 

2.安全发布的常用模式: 

 

 

 

 

 3.事实不可变对象:

如果对象的状态在发布后不会在改变,称为事实不可变状态。

 

 

 

 

 

 *********

 

 

 线程安全共享:此类内部实现同步。

 保护对象:使用时加锁。

posted @ 2017-06-02 11:23  上台阶  阅读(409)  评论(0编辑  收藏  举报