java并发编程实战-对象的共享

来源:互联网 发布:centos和redhat的区别 编辑:程序博客网 时间:2024/06/05 03:29

1,可见性

  1.1,JVM允许将64位的读操作或写操作分解为两个32位的操作。当读取一个非volatile类型的变量(long或者double)时,如果对该变量的读和写操作在不同的线程中执行,那么很可能读取到的是某个值的高32位和另一个值的低32位。因此,及时不考虑数据失效的问题,在多线程中使用共享切可变的long和double等数据类型变量也是不安全的,需要用关键字volatile来声明,或者用锁保护起来。
  1.2,加锁与可见性:加锁的含义不仅仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作或者写操作的线程都必须在同一个锁上同步。
  1.3,volatile变量不参与重排序。
    1.3.1,对于服务器应用程序,3无论在开发阶段还是测试阶段,当启动jvm时一定要指定-server命令行选项,server模式的jvm将比client模式的jvm进行更多的优化,如将循环中的未做修改的变量提升到循环外部,例如在while(asleep)中如果asleep变量没有声明为volatile类型,则server模式的jvm将会把asleep的判断条件提升到循环体外,造成while循环为一个无限循环。
    1.3.2,volatile变量通常用做某个操作完成,发生中断或者状态的标志,volatile的语义不足以保证递增操作(i++)的原子性,加锁机制既可以保证可见性也可以保证原子性,而volatile变量只能保证可见性,所以不要过多依赖volatile来保证并发操作,而更多的应当选择加锁机制来完成多线程程序。
    1.3.3,当且仅当满足一下所有条件时才应该使用volatile变量:
      a,对变量的读写操作不依赖变量的当前值,或者你能保证只有一个单线程更新变量值
      b,该变量不会与其他状态变量一起纳入不变性条件中
      c,在访问变量时不需要加锁

2,发布与溢出

  2.1,发布指的是使对象能够在当前作用域之外的代码中使用
  2.2,溢出指的是某个不应该发布的对象被发布了
  2.3,发布一个对象有几种方法:
    2.3.1,将对象的引用保存到一个公有的静态变量中
    2.3.2,当把一个对象传递给某个外部方法时,就相当于发布了这个对象。
    2.3.3,当发布了一个类的内部类实例,就会发布该对象
  2.4,安全对象构造过程
    2.4.1,在在构造函数未完成前发布对象,只是发布了一个尚未构造完全的对象
    2.4.2,在构造函数中启动一个线程,一定会造成溢出
3,线程封闭
  3.1,线程封闭指的是仅在单线程中访问的数据。封闭在单个线程中的数据是线程安全的
  3.2,线程封闭的三种方式:Ad-hoc线程封闭、栈封闭、ThreadLocal封闭
    3.2.1,Ad-hoc线程封闭:维护线程封闭性的职责完全由程序实现来承担 
    3.2.2,栈封闭:就是指在局部变量,局部变量只存在于线程栈中,因此其他线程是无法访问的
    3.2.3,ThreadLocal封闭:ThreadLocal这个类能使线程中的某个值于保存值得对象关联起来,因此每个线程再ThreadLocal中都有一个独自的副本。
4,不变性
  4.1,满足同步需求的另外一个方法是使用不可变对象,不可变对象一定是线程安全的。
  4.2,满足下面条件后,对象才是不可变的
    4.2.1,对象创建后其状态就不能修改
    4.2.2,对象的所有域都是final类型
    4.2.3,对象是正确创建的(在对象创建期间,this引用没有逸出)
  4.3,对于访问和更新多个相关变量时出现竞争条件问题,可以通过将这些变量全部保存在一个不可变对象中来消除。
5,安全发布
  5.1,任何线程都可以在不需要额外同步的情况下安全地访问不可变对象,及时在发布这些对象时没有使用同步。
  5.2,可变对象必须通过安全的方式来发布,可以通过下面方式来安全的发布:
    5.2.1,在静态初始化函数中初始化一个对象引用
    5.2.2,将对象的引用保存到volatile类型的域或者AtomicReferance对象中
    5.2.3,将对象的引用保存到某个正确构造对象的final类型域中
    5.2.4,将对象的引用保存到一个由锁保护的域中
  5.3,线程安全库中的容器提供一下安全发布保证:
    5.3.1,通过将一个键或者值放入Hashtable,synchronizedMap或者ConcurrentMap中,可以安全地将它发布给任何从这些容器中访问它的线程
    5.3.2,通过将某个元素放入Vector,CopyOnWriteArrayList,CopyOnWriteArraySet,synchronizedList或者synchronizedSet中,可以将该元素安全地发布到任何从这些容器中访问该元素的线程
    5.3.3,通过将某个元素放入BlockingQueue或者ConcurrentLinkedQueue中,可以将该元素安全地发布到任何从这些队列中访问该元素的线程
  5.4,要发布一个静态构造的对象,最简单和最安全的方式是使用静态的初始化器。静态初始化器由JVM在类的初始化阶段执行。由于在Jvm内部存在着同步机制,因此通过这种方式初始化任何多想都可以被安全地发布。
  5.5,事实不可变对象,如果对象从技术上来看是可变的,但是其状态在发布后不会再改变,那么就称该对象是事实不可变对象,在没有额外同步的情况下,任何线程都可以安全的使用被安全发布的事实不可变对象。
  5.6,可变对象在安全发布后,要确保每次对象访问时同样需要使用同步来确保后续修改操作的可见性。
总结1
  1.1,不可变对象可以通过任意机制来发布
  1.2,事实不可变对象必须通过安全方式来发布
  1.3,可变对象必须通过安全方式来发布,并且必须是线程安全的或者有某个锁保护起来
总结2
  2.1,线程封闭:线程粉封闭的对象只能由一个线程拥有,对象被封闭在线程中,并且只能由这个线程修改
  2.2,只读共享:在没有额外同步的情况下,共享的只读对象可以由多个线程并发访问,任何线程都不能修改他。共享的只读对象包括不可变对象和事实不可变对象
  2.3,线程安全共享:线程安全的对象在其内部实现同步,因此多个线程可以通过对象的公有接口来进行访问而不需要进一步的同步
  2.4,保护对象:被保护的对象只能通过持有特定的锁来访问。保护对象包括封装在其他线程安全对象中的对象,以及已发布的并且由某个特定锁保护的对象。