首页>>后端>>java->java并发:volatile和synchronized的本质区别是什么?

java并发:volatile和synchronized的本质区别是什么?

时间:2023-12-01 本站 点击:0

volatile和synchronized的本质区别是什么?

volatile和synchronized的唯一区别是,volatile只确保内存可见性,synchronized也确保内存可见性——并且还确保不同互斥,即同一时间,只能有一个线程获取到锁,其他线程阻塞等待获取锁。

与synchronized的区别?

synchronzied能确保共享数据同一时间只有一个线程可以更新共享数据。

但是volatile不能,因为volatile只能确保单个读或写操作是原子,但是对一个共享数据的修改,往往很多时候,需要依赖共享数据的旧值,比如在旧值之上加1,即先读共享数据的旧值——然后加1——最后写,这里实际上就包含了3个操作,所以volatile不能保证真正的同步。

volatile应用场景?

那volatitle应用场景是啥样的?对共享数据修改的时候,不依赖共享数据(本质是不使用共享数据的旧值),并且右边的变量也要确保线程安全(本质是因为右边的变量相当于是一个读的操作,那么更新共享数据的时候,就包含了2个操作:1、先读右边变量的值 2、再更新共享数据。更新操作是原子,但是多了一个读右边变量的操作,就不是原子了,因为现在包含了2个原子操作)。

参考:https://titanwolf.org/Network/Articles/Article?AID=6109465a-ab8f-4f01-b193-caaf5a1a79d6#gsc.tab=0 //这篇文章的作者就是写java并发编程实战的

示例

示例2-AtomicLong

说明:jdk并发包实现的AtomicLong的数据是volatile,就相当于我们自己定义数据的时候用valotile是一样的,只不过现在是jdk并发包的AtomicLong帮你做了这个事情,但是作用和本质是一样的。

单个读或写是原子操作

为什么单个读或写是原子操作?这个是java规范保证的。

准确的说,非long/double数据,才是原子。为什么?因为long/double是64位,而java规范只保证32位才是原子操作。

volatile的作用?

使得共享数据被一个线程更新的时候,其他线程可以立即读到最新值(即所谓的可见性)。即,没加volatile,可能读不到最新值;加了,就确保可以读到最新值。

syncronized的作用?

确保互斥,即同一时间,只能有一个线程更新共享数据。具体到代码层面就是,比如锁定一个更新共享数据的方法或者代码块,同一时间,只有一个线程可以执行这个方法或者代码块,这样就确保了其他线程获取不到锁,也就不能执行方法或者代码块,也就不能更新共享数据。

可见性呢?synchronized也可以确保可见性,可见性是指,线程A修改了共享数据,线程B可以看到最新数据。这似乎是一句废话,事实也是一句废话——这里主要是涉及到java内存模型的问题。

java内存模型

对Java内存模型的理解,以及其在并发中的应用。

Java内存模型的主要目标: 定义程序中各个变量的访问规则。

Java线程之间的通信由Java内存模型(本文简称为JMM)控制。

所有变量的存储都在主内存,每条线程还都有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作必须在工作内存完成,而不能直接读取主内存中的变量。不同的线程直接无法访问对方工作内存中的变量,线程间变量的传递均需要通过主内存来完成。

线程间通信:

首先,线程A把本地内存A中更新过的共享变量刷新(即写)到主内存中去。 //cpu1(线程1)——缓存1(线程工作内存1)——主内存

然后,线程B到主内存中去读取(即读)线程A之前已更新过的共享变量。//cpu2(线程2)——缓存2(线程工作内存2)——主内存

线程只能和线程工作内存通信,线程工作内存然后又和主内存通信。

不同线程的工作内存,不能互相通信,必须通过主内存。比如,线程1写数据到线程工作内存1,然后线程工作内存写数据到主内存;线程工作内存2从主内存读数据,然后线程2就可以从线程工作内存2读数据了。


架构图

cpu维度-架构图

线程维度-架构图

说明:线程维度的架构图可能不好理解,因为为什么会有这个架构图?为什么是这样子?本质是因为cpu维度的架构图。

而cpu维度的架构图的本质是,现在都是多cpu,每个cpu都有自己的高速缓存,为什么每个cpu都要有自己的高速缓存?不是已经有了内存(即架构图里的主内存)吗?因为高速缓存快啊,高速缓存比内存快,而且高速内存比内存要更小,内存越小越快。

各种存储设备的访问速度如下:

寄存器的速度最快,可以在一个时钟周期内访问,

其次是高速缓存,可以在几个时钟周期内访问,

普通内存可以在几十个或几百个时钟周期内访问。

https://zhuanlan.zhihu.com/p/37749443

内存可见性

内存模型这个东西很虚,也没什么意义,主要是不了解内存模型,就不能理解线程的内存可见性——因为不知道为什么,线程1修改了共享数据,但是线程2却不一定能读到共享数据的最新值。原因就在于内存模型,即上面的cpu维度的架构图里提到的,在cpu/线程和内存之间,还多了一个中介内存(即高速缓存/线程工作内存)。

多了一个中介,就多了一个数据不一致的问题。

那怎么解决数据不一致的问题?synchronized或者volatile,都能保证线程1修改共享数据之后,线程2就可以读到共享数据的最新值。

而synchronized和volatile的唯一区别就是,synchronized还多了一个功能,就是互斥的功能,即同一时间,只有一个线程能获取对象的锁,其他线程只能等待该线程释放锁——本质是同一时间,只能有一个线程执行方法或者代码块:即只能有一个线程,更新共享数据。

参考

https://segmentfault.com/a/1190000020770155

https://www.zhihu.com/question/38816432


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/java/5445.html