JAVA并发Volatile

概要

并发编程的关键性问题---通信与同步

并发就是让多个线程同时执行,若线程之间是相互独立的,那么并发也比较简单,各自执行各自的;但是当多个线程之间需要共享数据,此时在并发编程中就必须考虑两个问题,就是通信和同步。

通信:通信指消息在线程之间的传递;既然要传递消息,那么接受线程和发送线程之间必须有一个先后顺序,此时就需要使用同步,因此通信和同步相辅相成。

同步:同步就是指多个线程之间的执行顺序。

通信方式

通信的种类

通信之间有两种传递机制:共享内存和消息传递。

共享内存:共享内存指多个线程共享一个内存区,发送者将内容写入内存区,接受者直接在内存区接受信息。从而实现了消息的传递。这种传递有个弊端:即需要程序员来控制线程的同步---线程     的次序。然而这种方式并没有真正的实现消息传递,只是看起来是将消息从一个线程传到另一个线程中。

消息传递:指发送的线程直接将消息传递给接受线程,由于执行顺序的并发机制完成,不需要程序员来控制。

通信方式

JAVA是使用的是共享内存的方式进行通信。因此程序员需要写额外的代码来控制线程之间的同步。有两个要点:①:需要一个共享内存②:必须要实现线程同步。

所有线程都共用同一个内存区域,用于存储变量。但是各自线程都有自己的存储空间:存储各自的局部变量,方法参数,异常对象。

Volatile

JAVA采用共享内存的方式实现线程之间的消息传递,但是实现这些需要将共享内存依托于同步。java提供了synchronize和volatile来实现同步。然而volatile还拥有一些额外的功能。

使用方法:直接在前面添加volatile

private volatile boolearn fag=true

Volatile的特性

重排序:重排序指计算机为了提高程序执行率而对代码的执行顺序做出的调整,具体请看JAVA并发概要

若两行之间没有数据依赖,那么编译器和处理器可以对他们的顺序做出排序,但是当时volatile修饰时,那么重排序的规则发生改变。

以下两种关系,尽管两个之间依赖关系,也不会进行重排序了。

volatile读

  • 若volatile的读操作的前一条为volatile读/写,那么这两条不会进行重排序。
  • volatile读和他的后一条代码都不会发生重排序。

volatile写

  • 若volatile的写操作的后一条为volatile读/写,那么这两条不会发生重排序。
  • volatile写和他的前一条代码都不会发现重排序。

可见性

内存可见性指一条线程对共享变量的修改,对其他线程可见。

但是如果共享变量没有采用同步机制,那么是内存不可见的。

为什么会出现内存不可见的情况

每一个线程都有一个独立的存储空间,还有一个全部线程的共享存储空间。

但开启线程时,系统将共享变量拷贝到线程的独立存储空间中。然而接下来的所有操作都是基于独立的存储空间中,所有线程对共享变量的修改等操作,其他线程访问时,其值还是先前没有改变的值。我们希望一个线程修改其值后,其他线程能够立即访问这个新值,那么需要同步机制来解决这个问题。

怎么保证共享变量的可见性。

要确保所有变量都是可见 的,就要给共享变量实现同步。在JAVA中可以选择共享变量使用同步代码块和volatile修饰。

为什么volatile能保证共享变量的内存可见性?

因为volatile修饰共享变量后,这个变量的读写比普通的变量多一些步骤。

volatile变量写:当被volatile写时,这个变量直接写入共享内存,而非线程专属存储空间中读。

volatile变量读:当读取一个被volatile的变量时,会直接在共享内存中读。

多余的操作:

进行volatile写操作时,不仅会将volatile变量写入共享内存,系统还会将当前线程专属空间中的所有共享变量写入共享内存。
进行volatile读操作时,系统也会一次性将共享内存中所有共享变量读入线程专属空间。
这就意味着,如果普通变量在volatile写操作之前被修改,那么在volatile读操作之后就能正确读到他们。
但是,在volatile写操作之后被修改的普通变量 和 在volatile读操作之前被访问的普通变量 都不具有内存可见性。

原子性

指一组操作必须一起完成,中途不能被中断。

posted @ 2018-09-17 20:57  轻抚丶两袖风尘  阅读(236)  评论(0编辑  收藏  举报