一道有深度的面试题:本地悲观锁实现计数器需要加 volatile 吗?

故事背景

团队内部前几天讨论了一个面试题,在本地用乐观锁和悲观锁实现计数器需要volatile关键字吗?毫无疑问,使用乐观锁一定是需要的。但使用悲观锁需要呢?

张三:不需要吧,每次不都是一个线程访问变量吗?

李四:还是需要的,加锁只是保证了该变量被一个线程独占,但是不能保证拿到变量最新的值,因为可能上个线程操作后数据还在线程本地内存里,导致本线程读取的数据是脏数据!

张三:嘶,好像有点道理,不过如果加锁的话,这个本地内存的数据什么时候刷到主内存呢?会不会加锁后就直接读取到最新数据了?

李四:诶?问得好,这个得研究研究。

预备知识

Hppens-Before 规则

Java Memory Model(JMM) 里定义了一些跨线程操作的 Happens-Before 关系,并据此来决定线程间一些操作的相对顺序。如果说操作 A “Happens-Before” B,则有两个含义:

  1. 可见性:A 的操作对 B 可见
  2. 顺序性:A 要在 B 之前执行

Happens-before 规则有多条,本文只借助几条来进行解释

  • 程序顺序规则:如果程序中操作 A 在操作 B 之前,那么在线程中操作 A Happens-Before 操作 B
  • 监视器锁规则:监视器上的 unlock 操作 Happens-Before 同一个监视器的 lock 操作
  • volatile 变量规则:写入 volatile 变量 Happens-Before 读取该变量
  • 传递性:如果 hb(A, B)hb(B, C),则 hb(A, C)

volatile 的内存语义

从内存语义的角度来说,volatile的写-读与锁的释放-获取有相同的内存效果。

volatile 写的内存语义:当写一个 volatile 变量时,JMM 会把该线程对应的本地内存中写 volatile 前所有的共享变量刷新到主内存中,并让其他 core 的缓存失效,不管这些变量是否volatile,不仅仅只是 volatile 变量本身。

volatile 读的内存语义:当读一个 volatile 变量时,JMM 会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。

关于 volatile 写的说明:https://jenkov.com/tutorials/java-concurrency/volatile.html

synchronized 实现

synchronized 可见性原理

Synchronized 的 Happens-Before 规则,即监视器锁规则:对同一个监视器的解锁,Happens-Before 于对该监视器的加锁。

在这里插入图片描述

图中每一个箭头连接的两个节点就代表之间的 Happens-Before 关系,红色的为监视器锁规则推导而出:线程A释放锁 Happens-Before 线程B加锁;蓝色的则是通过程序顺序规则和监视器锁规则推测出来 Happens-Before 关系,通过传递性规则进一步推导的 Happens-Before 关系。

根据 Happens-Before 规则的程序顺序规则:如果 A Happens-Before B,则 A 的执行结果对 B 可见,并且 A 的执行顺序先于 B。因此,如果线程 A 修改了计数器的值,对线程 B 是可见的。

synchronized 验证代码

public class TestSynchronizedCounter {
   
   
    private static int count = 0;

    public static void main(String[] args) {
   
   
        new Thread(() -> {
   
   
            for (int i = 0; i < 100000; i++) {
   
   
                synchronized (TestSynchronizedCounter.class) {
   
   
                    count++;
                }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笼中小夜莺

嘿嘿嘿,请用金钱尽情地蹂躏我吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值