volatile关键字与线程间通信

简介:

1.Java内存模型

现在计算机普遍使用多处理器进行运算,并且为了解决计算机存储设备和处理器的运算速度之间巨大的差距,引入了高速缓存作为缓冲,
缓存虽然能极大的提高性能,但是随之带来的缓存一致性的问题,
例如,当多个处理器同时操作同一个内存地址,可能会导致各自的缓存数据不一致,由此产生冲突问题,
内存模型就是定义一套充分必要的规范,这些规范使得其他处理器对内存的写操作对当前处理器可见,或者当前处理器的写操作对其他处理器可见。

类似物理上的计算机系统,Java虚拟机规范中也定义了一种Java内存模型,即Java Memory Model(JMM),来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果。

现在最新的Java内存模型规范是JSR-133,即Java内存模型与线程规范

这套规范包含:
线程之间如何通过内存通信;
线程之间通过什么方式通信才合法,才能得到期望的结果。

2.线程间通信

线程间通信的方式有很多种,Windows下进程间通信及数据共享,这里只考察共享内存的方式。
对于Java的并发编程,线程间采用的是共享内存通信,
在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信。

3.volatile关键字和内存结构

先看一下Java内存模型对应的内存结构:

线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存中存储了该线程以读/写共享变量的副本。

本地内存是一个抽象的概念,包括缓存、写缓冲区、寄存器等,

一个线程对 volatile 变量的写一定对之后对这个变量的读的线程可见,
即线程对 volatile 变量的读一定能看见在它之前最后一个线程对这个变量的写。

volatile包含以下语义:

(1)Java内存模型不会对valatile指令的操作进行重排序:这个保证对volatile变量的操作时按照指令的出现顺序执行的。
(2)volatile变量不会被缓存在寄存器中(只有拥有线程可见)或者其他对CPU不可见的地方,每次总是从主存中读取volatile变量的结果。

为了实现这些语义,Java 规定,(1)当一个线程要使用共享内存中的 volatile 变量时,如图中的变量a,它会直接从主内存中读取,而不使用自己本地内存中的副本。(2)当一个线程对一个 volatile 变量进行写时,它会将这个共享变量的值刷新到共享内存中。

volatile 变量保证的是一个线程对它的写会立即刷新到主内存中,并置其它线程的副本为无效,它并不保证对 volatile 变量的操作都是具有原子性的

volatile 变量的写可以被之后其他线程的读看到,因此我们可以利用它进行线程间的通信。如

1
2
3
4
5
6
7
volatile  int  a;
public  void  set( int  b) {
a = b;
}
public  void  get() {
int  i = a;
}


线程A执行set()后,线程B执行get(),相当于线程A向线程B发送了消息。

volatile的语义,其实是告诉处理器,

不要将我放入工作内存,请直接在主存操作,多线程在访问该变量时,都将直接操作主存,这从本质上,做到了变量共享

4.synchronized关键字

synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程A每次运行到这个方法时,都要检查有没有其它正在用这个方法的线程B(或者C D等),有的话要等正在使用这个方法的线程B(或者C D)运行完这个方法后再运行此线程A,没有的话,直接运行。
它包括两种用法:synchronized 方法和 synchronized 块。

5.final关键字

final关键字可以作用于变量、方法和类,我们这里只看final 变量。
final变量的特殊之处在于,final 变量一经初始化,就不能改变其值。
这里的值对于一个对象或者数组来说指的是这个对象或者数组的引用地址。因此,一个线程定义了一个final变量之后,其他任意线程都拿到这个变量。

但有一点需要注意的是,当这个final变量为对象或者数组时,
虽然我们不能讲这个变量赋值为其他对象或者数组,但是我们可以改变对象的域或者数组中的元素。
线程对这个对象变量的域或者数据的元素的改变不具有线程可见性。

Java内存模型对final有不用的重排序规则,final引用不能从构造函数内“逸出”。

 


本文转自邴越博客园博客,原文链接:http://www.cnblogs.com/binyue/p/5232066.html,如需转载请自行联系原作者

相关文章
|
10天前
|
存储 Java 数据库连接
java多线程之线程通信
java多线程之线程通信
|
1月前
|
Python
如何在Python中实现线程之间的同步和通信?
【2月更文挑战第17天】【2月更文挑战第51篇】如何在Python中实现线程之间的同步和通信?
|
13天前
|
设计模式 安全 Java
Java并发编程实战:使用synchronized关键字实现线程安全
【4月更文挑战第6天】Java中的`synchronized`关键字用于处理多线程并发,确保共享资源的线程安全。它可以修饰方法或代码块,实现互斥访问。当用于方法时,锁定对象实例或类对象;用于代码块时,锁定指定对象。过度使用可能导致性能问题,应注意避免锁持有时间过长、死锁,并考虑使用`java.util.concurrent`包中的高级工具。正确理解和使用`synchronized`是编写线程安全程序的关键。
|
4月前
|
Java
线程间通信之Object.wait/notify实现
线程间通信之Object.wait/notify实现
28 0
|
4月前
|
消息中间件 存储 Unix
进程间通信和线程间通信总结
写在前面 面试的时候一定不要疲劳战,比如上午面了一个,然后中午不休息直接赶到另外一个相距比较远的公司,影响状态。 面试的时候一定不要紧张,不管对方有几个人,总之面试的时候做好充分准备,休息好,放松心态。 好了,言归正传,开始总结。
39 0
|
30天前
|
存储 缓存 安全
【C/C++ 关键字 存储类说明符 】 线程局部变量的魔法:C++ 中 thread_local的用法
【C/C++ 关键字 存储类说明符 】 线程局部变量的魔法:C++ 中 thread_local的用法
33 0
|
1月前
|
存储 安全 Java
并发编程知识点(volatile、JMM、锁、CAS、阻塞队列、线程池、死锁)
并发编程知识点(volatile、JMM、锁、CAS、阻塞队列、线程池、死锁)
71 3
|
30天前
|
消息中间件 并行计算 网络协议
探秘高效Linux C/C++项目架构:让进程、线程和通信方式助力你的代码飞跃
探秘高效Linux C/C++项目架构:让进程、线程和通信方式助力你的代码飞跃
33 0
|
1月前
|
存储 Java 数据库连接
线程通信(CountDownLatch、CyclicBarrier、Semaphore、Exchanger)
线程通信(CountDownLatch、CyclicBarrier、Semaphore、Exchanger)
34 0
|
1月前
|
安全
多线程通信
多线程通信