Java并发编程——共享对象

1、可见性

只要数据需要被跨线程共享,就进行恰当的同步。

一个线程在没有同步的情况下读取变量,它可能会得到一个过期值。但它至少可以看到某个线程在那里设置的一个真实数值,而不是一个凭空而来的值。这样的安全保证被称为是最低限的安全性。

锁不仅仅是关于同步与互斥的,也是关于内存可见的。为了保证所有线程都能够看到共享的、可变变量的最新值,读取和写入线程必须使用公共的锁进行同步。

只有当volatile变量能够简化实现和同步策略的验证时,才使用它们。当验证正确性必须推断可见性问题时,应避免使用volatile变量。正确使用volatile变量的方式包括:用于确保它们锁引用的对象状态的可见性,或者用于标识重要的生命周期时间(比如初始化或关闭)的发生。

   volatile boolean asleep;

   …

   while(!asleep)

     countSomeSheep();

加锁可以保证可见性与原子性;volatile变量只能保证可见性。

只有满足下面所有标准后,你才能使用volatile变量:

1)写入变量时并不依赖变量的当前值;或者能够确保只有单一的线程修改变量的值;

2)变量不需要与其他的状态变量共同参与不变约束;

3)而且,访问变量时,没有其他的原因需要加锁。

2、发布和逸出

发布一个对象的意思是使它能够被当前范围之外的代码锁使用。一个对象在尚未准备好时就将它发布,这种情况称作逸出。

3、线程封闭

当对象封闭在一个线程中时,这种做法会自动成为线程安全的,即使被封闭的对象本身并不是。

一种维护线程限制的更加规范的方式是使用ThreadLocal,它允许你将每个线程与持有数值的对象关联在一起。ThreadLocal提供了get与set访问器,为每个使用它的线程维护一份单独的拷贝。所以get总是返回由当前执行线程通过set设置的最新值。

4、不可变性

不可变对象永远是线程安全的。

只有满足如下状态,一个对象才是不可变的:

1)它的状态不能在创建后再被修改;

2)所有域都是final类型;

3)它被正确创建(创建期间没有发成this引用的逸出)。

5、安全发布

为了安全的发布对象,对象的引用以及对象的状态必须同时对其他线程可见。一个正确创建的对象可以通过下列条件安全地发布:

1)通过静态初始化器初始化对象的引用。

2)将他的引用存储到volatile域或AtiomicReference。

3)将它的引用存储到正确创建的对象的final域中。

4)或者将它的引用存储到由锁正确保护的域中。



留言