2024年Java最新Java并发01---JMM模型、Volatile、CAS操作、自旋锁、ABA问题,2024BAT大厂Java社招最全面试题

总结

我们总是喜欢瞻仰大厂的大神们,但实际上大神也不过凡人,与菜鸟程序员相比,也就多花了几分心思,如果你再不努力,差距也只会越来越大。

面试题多多少少对于你接下来所要做的事肯定有点帮助,但我更希望你能透过面试题去总结自己的不足,以提高自己核心技术竞争力。每一次面试经历都是对你技术的扫盲,面试后的复盘总结效果是极好的!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

每个线程在执行的时候首先会将数据从主内存中加载至私有内存然后再进行操作。

Volatile修饰

知道JMM模型的主要内容之后,让我们想象一个Java使用场景:

  1. 首先线程A创建对象Integer a = 10;此时JVM会为a在堆中分配一块内存,此时线程A运行结束。
  2. 线程B,线程C被创建,这两者都要对a进行修改,所以JVM会将a复制到B和C的栈中,BC两者分别都对a进行a = a + 1操作,在B中此时a=2,操作结束之后a=3,在C中此时a=2,操作结束之后a=3,操作结束之后这两个线程会将a写回堆内存中。此时问题就发生了,a虽然进行了两次+1操作但是结果仍然是3。

为了解决这个问题我们可以对a进行volatile修饰,volatile关键字会使被修饰的变量有两个特点:

  1. 变量在内存中的可见性
    (1) 当有线程对volatile变量进行修改的时候JVM会强制将该变量在主内存中数据的进行刷新。
    (2) 这种刷新会让别的线程已被加载的该变量无效化。
  2. 禁止指令重排序

这样子似乎就能解决并发问题了,可是volatile虽然能保证变量在内存中的可见性,但是却无法保证原子性。

让我们再想象一个使用场景:

  1. 首先我们有volatile Integer a = 10;
  2. 线程A要进行以下操作a++;
  3. 线程B要进行以下操作a = 0;
  4. A与B并发执行,由于a++这个操作并不是原子操作,在实际执行的时候会先获得a值再进行自加,所以实际操作的过程中获得a值之后可能会被中断。理想的结果是A中的a最后等于11,但是假如A执行一半被中断了,转换为B去执行,B执行完之后A继续,那么此时结果就变成了1。

我们可以发现,volatile虽然很好,但是还不够好。为了解决上面的问题,我们可以对A线程加锁,使用sychronized对该代码段进行修饰,但是sychronized是一种阻塞式的独占锁悲观锁,在这里介绍一种非阻塞的乐观锁,i.e. 自旋锁,所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。在自旋锁内部里有一个非常重要的概念:CAS方法

CAS(Compare And Swap)

  1. 首先CAS是一种思想。
  2. 其次CAS是一种CPU的原子指令。
  3. 最后在Java的concurrent包中有一系列基于CAS的native方法,如compareAndSet

那么什么是CAS?

顾名思义,CAS(Compare And Swap)先要比较内存中实际的数值与预期数值是否相等,若相等则将内存中的实际值设置为新值。

来看代码示例:

使用Java的concurrent包中有原子类如AtomicInteger,其实现了CAS的操作方法。

AtomicInteger a = new AtomicInteger(10);
System.out.println(a.compareAndSet(10, 20) + " " + a);
System.out.println(a.compareAndSet(10, 30) + " " + a);

// 输出结果:
// true 20
// false 20

首先a为10,第一步compareAndSet预期值为10,实际值为10,可以成功将a设置为新值20。
第二步compareAndSet预期值为10,实际值为20,设置新值失败。

通过CAS操作,我们就可以避免上面提到的并发问题。

下面是一个基于CAS方法的乐观锁自增实现,摘自Java乐观锁的实现原理和典型案例

public class Counter {
    private AtomicInteger value = new AtomicInteger(0);//实际值

    public void increment() {
        int expect;
        int update;

        do {
            expect = value.get(); // 预期值
            update = expect + 1; // 新值
 // 比较value与expect是否相等,相等则使用update更新value
        } while (!value.compareAndSet(expect, update));
    }
}

在上述代码中如果在do代码块中被中断并且value被修改,则value不会被更新,继续循环,直到do代码块不被中断,则value可以被更新,循环结束。

不难看出,这段代码实际上就是一个乐观锁的实现,并且具有以下特点。

  1. 不会阻塞线程。i.e. 非阻塞
  2. 总是假设value没有被更新,若检测到value被更新了(线程冲突),则更新失败。i.e. 乐观

Kafka进阶篇知识点

image

Kafka高级篇知识点

image

44个Kafka知识点(基础+进阶+高级)解析如下

image

由于篇幅有限,小编已将上面介绍的**《Kafka源码解析与实战》、Kafka面试专题解析、复习学习必备44个Kafka知识点(基础+进阶+高级)都整理成册,全部都是PDF文档**

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/topics/618154847)收录**

需要这份系统化的资料的朋友,可以点击这里获取

  • 20
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值