第二章:并发编程-对象的共享

本章主要介绍内存可见性

前面讲解了同步代码块和同步方法可以确保原子的方式执行操作,但是同步还有另一个重要的方面:内存可见性。

我们不仅希望防止某个线程正在使用对象状态而另一个线程在同时修改该状态,而且希望确保当一个线程修改了对象状态后,其他线程能看到发生的状态变化。


1.可见性,volatile变量

缺乏同步的程序中可能出现失效数据,

非volatile类型的64位数值变量(double和long),JVM允许将64位的读操作和写操作分解为32位的操作,因此在多线程中使用共享且可变的long和double是不安全的。

加锁的含义不仅仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作和写操作的线程都必须在同一个锁上同步。


java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。

当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将这个变量的操作与其他内存操作一起重排序。volatile变量不会被缓存再寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。

加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性。

当满足以下所有条件时,才应该使用volatile变量:

1.对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值。

2.该变量不会与其他状态变量一起纳入不变性条件中

3.在访问变量时不需要锁。


2.线程封闭,ThreadLocal类

当访问共享的可变数据时,通常需要使用同步。一种避免使用同步的方式就是不共享数据。如果仅在单线程内访问数据,就不需要同步,这种技术被称为线程封闭。

ThreadLocal类,这个类可以使线程中的某个值与保存值的对象关联起来,ThreadLocal提供了get与set等访问接口或方法,这些方法为每个使用该变量的线程都存有一份独立的副本,因此get总是返回由当前执行线程在调用set时设置的最新值。

ThreadLocal对象通常用于防止对可变的单例变量或者全局变量进行共享。

当某个线程初次调用ThreadLocal.get方法时,就会调用initialValue来获取初始值。


3.不变性

满足同步需求的另一种方法是使用不可变对象,不可变对象一定是线程安全的。


猜你喜欢

转载自blog.csdn.net/dxh0823/article/details/79994505