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

一、可见性

当读操作和写操作在不同的线程进行的时候,并不能保证读的线程可以读到写线程最新的更改。如果要确保内存对写入操作的可见性,就必须使用同步

处理器还会对程序中的操作进行重排序。重排序保证在单线程的执行情况下,和不重排序得到的结果一样,但是多线程的话,就不一定了。

一个简单的方法避免所有的问题就是:只要有数据共享,就是用正确的同步

1.失效数据    看下面的程序:


如果不对set和get进行同步,肯定是不能保证可见性的。但是仅对set加锁,仍然可能会看到失效的数据。因为set同步后把数据刷新到主内存,但是get的时候,还是使用的工作内存,并没有去主内存取值

2.Volatile变量

volatile是一种弱同步机制,可以保证可见性,但是不能保证原子性。

volatile变量的正确使用方式是:

  • 确定他们自身状态的可见性
  • 确保他们所引用对象的状态的可见性
  • 标志一些重要的生命周期事件的发生(初始化,关闭
当且仅当满足以下条件时,才可以使用volatile变量
  • 对变量的更新操作不依赖于变量得当前值,或者只有单线程修改变量的值
  • 该变量不应该与其他状态变量一起纳入不变性条件
  •       访问该变量时,不需要加锁
二、发布和溢出
发布就是使对象能够在当前的作用域之外的代码中使用。在一个非私有(非private和final方法)的方法中返回该引用,或者将对象引用保存到其他代码可以访问到的地方。
发布状态最简单的方法就是:将对象引用保存到共有的静态变量中。public static Set<String> aset;
发布对象之后,不知道外部代码会对对象做什么操作,所以勿用该对象的风险一直存在

2.安全的对象构造过程
不要在构造过程中使this引用逸出。比如在构造过程中启动一个线程。就是,构造函数中,不要使用this的相关引用

三、线程封闭

一种避免使用同步的方式,就是不共享数据。如果仅在单线程内访问数据,就不需要同步。这种技术称为线程封闭。三种方式:ad-hoc,栈封闭,ThreadLocal。

1.栈封闭

就是可重入代码,方法内只包含局部变量(形参和方法内定义的变量),那么这段代码就一定是线程安全的。因为不能获得基本类型变量的引用,所以java可以保证基本类型的局部变量封闭在线程中。


维持对象引用的封闭性时,比如上面的animals对象,要确保栈封闭性,就要确保animals不会被发布到外面去。那么这段代码也是栈封闭的。

3.ThreadLocal类

ThreadLocal常用与防止对可变的单实例变量或全局变量进行共享。当某个频繁的操作需要一个临时对象,比如说缓冲区,而同时又希望避免在每次执行时,都重新分配该临时对象,就可以使用这种技术。

ThreadLocal类类似于全部变量会,降低代码的可重用性。

四、不变性

如果某个对象创建后,其状态就不能修改,我们称之为不可变对象。不可变对象一定是线程安全的

当满足以下条件时,对象才是可变的:

  • 对象创建以后,状态就不能改变
  • 对象的所有域都是final类型的
  • 对象是正确的创建(对象创建期间,没有this引用逸出)

不可变的对象并不是不可变的对象引用。

2.final域

final类型的域中可以保存对可变对象的引用。final域能够确保初始化过程的安全性,从而可以不受限制的访问不可变对象,并在共享这些对象时,不需要使用同步。

除非需要某个域是可变的,否则应该将其声明为final域,是一个比较好的编程习惯。

当访问和更新多个相关变量时出现竞态条件,可以通过把这些变量都保存在一个不可变对象中来消除。



当需要更新这个变量的时候,在重新new一个不可变对象就好了。



五、安全发布

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

在对象被完全构造出来之前,将其发不出去。

2.不可变对象与初始化安全性

我们知道:即使某个对象的引用对其他线程来说是可见的,也不意味着对象状态对于该线程一定是可见的。为了使对象状态呈现出一致性视图,必须使用同步。

即使再发布不可变对象时,没有使用同步,也可以保证这种可见性。可以延伸到final类型的域中

3.安全阀不得常用模式

  • 在静态初始化函数中初始化一个对象引用
  • 将对象的引用保存到volatile或者atomicreference中
  • 将对象的引用保存到某个,被正确构造的final类型的域中
  • 将对象的引用保存到一个锁保护的域中

4.实事不可变对象

如果对象从技术上来说是可变的,但是从它发布以后,就不再改变。这种对象称为事实不可变对象。不需要额外同步的情况下,任何线程都可以安全的访问实事不可变对象。

5.可变对象

要安全的共享可变对象,这些对象就必须被安全的发布,并且必须是线程安全或者某个锁保护起来。

对象的发布需求取决于它的可变性:

不可变对象可通过任意机制发布。

实事不可变对象必须通过安全方式发布。

对象就必须被安全的发布,并且必须是线程安全或者某个锁保护起来可变。

6.安全的共享对象

当发布一个对象的时候,必须明确地说明对象的访问方式。

线程封闭

只读共享

线程安全共享

保护对象


猜你喜欢

转载自blog.csdn.net/liang0000zai/article/details/51395429