读书笔记-如何正确的发布并发对象

来源:互联网 发布:图片热点 js 边框 编辑:程序博客网 时间:2024/04/30 05:04

本文是对《java并发编程实战》第三章对象的共享的整理,并不解释java的并发编程原理,而是说明从编程技巧上如何实现更好的发布对象。当然通常情况下我们考虑如何正确的使用同步组件去写出正确的同步代码,但是即使不适用同步组件也有很多其他方法来保证正确的访问代码。本文主要从线程封闭和不变性的角度考虑如何写出正确的多线程程序。

发布与逸出

发布一个对象的意思是指,使对象能够在当前作用域之外的代码中使用。
逸出是指当某个不该发布的对象被发布的时候。

能否保证代码的正确性和同步的正确性当然重要,但是更为重要的一点是在编写方法的时候注意不要使得内部数据的引用外漏,能操作内部引用的就只有通过这个对象的方法。如果引用泄露了。那无论如何编写程序也无法保证其正确性。

以上的内容当然是基本的编程原理,大部分人都能掌握,但是两点需要注意,

  1. 慎重的使用this。尤其在构造内部类的时候,使用内部类的情况通常都是需要访问内部类,但是需要注意的是,这样一来数据就泄露给了内部类。
  2. 不要在构造过程中使用this。因为这会导致不一致性,如果必要请使用其他方法比如说使用一个初始化方法,或者使用(静态)工厂方法去实例化。

线程封闭

如果多线程程序仅仅通过一个线程去访问。也就相当于是单线程程序,这是实现正确同步程序的有效方法。这句话其实相当于是废话,但是具体如何去实现确有一些技巧。可以分为3中方法

  1. Ad-hoc线程封闭
    其实也就是全部通过程序来控制,者当然是不安全的。典型的情况下是使用一个volatile作为共享的变量。仅有一个线程有写这个volatile变量的权利,而其他的所有线程都只能读取这个值。
  2. 栈封闭
    其实就是仅仅使用局部变量,这样当然也是安全的
  3. ThreadLocal类
    更规范的维护线程类的方法。这个类可以使得线程中的某个值与保存值的对象关联起来。它和某个线程绑定相当于Map<Thread,T>。通常可以用来替代共享的全局变量。而且由于不需要锁也不需要新建或者从有限的资源池中存取效率高很多。典型的情况比如说数据库的连接就是一个很好的例子。

不变性

如果对象是不可变对象,那么它一定是线程安全的。

首先需要明确的是不变性并不是final。即使是final类型,如果该域保存有其他可变对象的引用则这个域依然是可以变化的,典型的如容器类。。通常来说java的不变指的是:

  1. 当对象改变后其状态就不能修改
  2. 对象的所有域都是fianl类型
  3. 对象是正确创建的(在对象创建期间,this引用没有逸出)

    对于final域,final域可以确保初始化过程的安全性,从而可以不受限制的访问不可变对象,并在共享时无需同步,因此应该在所有可以加final的域前加上final。

    当然如果对象是不可变的就是线程安全的,但是这句话似乎并没有什么意义,因为很多时候我们并没有办法保证其是可变的,但是由于final类的特效,他在某种程度上提供了一定程度的原子性。
    比如对于有首先我使用volatile变量B的引用定义在主类A中作为A的一个域,然后将B的所有类都指定为final域,这样一来每次变化的时候只要重新建立一个B类替换原先的类就可以了。由于final类必定是一次赋值的所以不可能出现不一致的情况。

正确的发布对象

对于不可变对象,如果只要满足三个条件创建出不可变对象,即使不附加额外的同步语句也可以正确的访问对象。

对于可变对象,一个正确的构造对象可以有以下四种方式来安全的发布对象:

  1. 在静态初始化函数中初始化一个对象引用
  2. 将对象的引用保存到volatile类型的域或者atomicReference对象中
  3. 将对象的引用保存到某个正确构造对象的final类型域中
  4. 将对象的引用保存到保存到一个由锁保护的域中。(放入同步容器内也相当于是这条)

    对于可变对象只能保证发布的当时状态的可见性。

0 0
原创粉丝点击