Java多线程之volatile详解,夯实你的开发基础,2024大厂Java开发面试总结+解答

public class ABTread {

private static volatile int num=0;

public static void main(String[] args) throws InterruptedException {

Thread A=new Thread(new Runnable() {

@Override

public void run() {

while (true){

if (num==0){ //读取num过程记作1

System.out.println(“A”);

num=1; //写入num记位2

}

}

}

},“A”);

Thread B=new Thread(new Runnable() {

@Override

public void run() {

while (true){

if (num==1){ //读取num过程记作3

System.out.println(“B”);

num=0; 写入num记位4

}

}

}

},“B”);

A.start();

B.start();

}

}

Lock可以通过阻止同时访问来完成对共享变量的同时访问和修改,必要的时候阻塞其他尝试获取锁的线程,那么volatile关键字又是如何工作,在这个例子中,是否效果会优于Lock呢。

Java 内存模型中的可见性、原子性和有序性

======================

  • **可见性:**值线程之间的可见性,一个线程对于状态的修改对另一个线程是可见的,也就是说一个线程修改的结果对于其他线程是实时可见的。

可见性是一个复杂的属性,因为可见性中的错误总是会违背我们的直觉(JMM决定),通常情况下,我们无法保证执行读操作的线程能实时的看到其他线程的写入的值。为了保证线程的可见性必须使用同步机制。退一步说,最少应该保证当一个线程修改某个状态时,而这个修改时程序员希望能被其他线程实时可见的,那么应该保证这个状态实时可见,而不需要保证所有状态的可见。在 Java 中 volatile、synchronized 和 final 实现可见性。

  • **原子性:**如果一个操作是不可以再被分割的,那么我们说这个操作是一个原子操作,即具有原子性。但是例如i++实际上是i=i+1这个操作是可分割的,他不是一个原子操作。

非原子操作在多线程的情况下会存在线程安全性问题,需要是我们使用同步技术将其变为一个原子操作。java的concurrent包下提供了一些原子类,我们可以通过阅读API来了解这些原子类的用法。比如:AtomicInteger、AtomicLong、AtomicReference等。在 Java 中 synchronized 和在 lock、unlock 中操作保证原子性

  • **有序性:**一系列操作是按照规定的顺序发生的。如果在本线程之内观察,所有的操作都是有序的,如果在其他线程观察,所有的操作都是无序的;前半句指“线程内表现为串行语义”后半句指“指令重排序”和“工作内存和主存同步延迟”

Java 语言提供了 volatile 和 synchronized 两个关键字来保证线程之间操作的有序性。volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行。

Volatile原理

==============

volatile定义:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该通过获取排他锁单独获取这个变量;

java提供了volatile关键字在某些情况下比锁更好用。

  • Java语言提供了volatile了关键字来提供一种稍弱的同步机制,他能保证操作的可见性和有序性。当把变量声明为volatile类型后,

编译器与运行时都会注意到这个变量是一个共享变量,并且这个变量的操作禁止与其他的变量的操作重排序。

  • 访问volatile变量时不会执行加锁操作。因此也不会存在阻塞竞争的线程,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。

volatile的特性

===========

volatile具有以下特性:

  • 可见性:对于一个volatile的读总能看到最后一次对于这个volatile变量的写

  • 原子性:对任意单个volatile变量的读/写具有原子性,但对于类似于i++这种复合操作不具有原子性。

  • 有序性:

volatile happens-before规则

=============================

根据JMM要求,共享变量存储在共享内存当中,工作内存存储一个共享变量的副本,

线程对于共享变量的修改其实是对于工作内存中变量的修改,如下图所示:

Java多线程之volatile详解,夯实你的开发基础

从多线程交替打印A和B开始章节中使用volatile关键字的实现为例来研究volatile关键字实现了什么:

假设线程A在执行num=1之后B线程读取num指,则存在以下happens-before关系

  1. 1 happens-before 2,3 happens-before 4

  2. 根据volatile规则有:2 happens-before 3

  3. 根据heppens-before传递规则有: 1 happens-before 4

至此线程的执行顺序是符合我们的期望的,那么volatile是如何保证一个线程对于共享变量的修改对于其他线程可见的呢?

volatile 内存语义

=================

根据JMM要求,对于一个变量的读写存在8个原子操作。对于一个共享变量的读写过程如下图所示:

Java多线程之volatile详解,夯实你的开发基础

对于一个没有进行同步的共享变量,对其的使用过程分为read、load、use、assign以及不确定的store、write过程。

整个过程的语言描述如下:

  • 第一步:从共享内存中读取变量放入工作内存中(readload)

  • 第二步:当执行引擎需要使用这个共享变量时从本地内存中加载至CPU中(use)

  • 第三步:值被更改后使用(assign)写回工作内存。

  • 第四步:若之后执行引擎还需要这个值,那么就会直接从工作内存中读取这个值,不会再去共享内存读取,除非工作内存中的值出于某些原因丢失。- 第五步:在不确定的某个时间使用storewrite将工作内存中的值回写至共享内存。

由于没有使用锁操作,两个线程可能同时读取或者向共享内存中写入同一个变量。或者在一个线程使用这个变量的过程中另一个线程读取或者写入变量。

上图中1和6两个操作可能会同时执行,或者在线程1使用num过程中6过程执行,那么就会有很严重的线程安全问题,

一个线程可能会读取到一个并不是我们期望的值。

**那么如果希望一个线程的修改对后续线程的读立刻可见,那么只需要将修改后存储在本地内存中的值回写到共享内存

并且在另一个线程读的时候从共享内存重新读取而不是从本地内存中直接读取即可;事实上

当写一个volatile变量时,JMM会把该线程对应的本地内存中共享变量值刷新会共享内存;

而当读取一个volatile变量时,JMM会从主存中读取共享变量**,这也就是volatile的写-读内存语义。

volatile的写-读内存语义:

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

如果将这两个步骤综合起来,那么线程3读取一个volatile变量后,写线程1在写这个volatile变量之前所有可见的共享变量的值都将乐客变得对线程3可见。

volatile变量的读写过程如下图:

Java多线程之volatile详解,夯实你的开发基础

需要注意的是:在各个线程的工作内存中是存在volatile变量的值不一致的情况的,只是每次使用都会从共享内存读取并刷新,执行引擎看不到不一致的情况,

所以认为volatile变量在本地内存中不存在不一致问题。

volatile 内存语义的实现

====================

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

总结

在清楚了各个大厂的面试重点之后,就能很好的提高你刷题以及面试准备的效率,接下来小编也为大家准备了最新的互联网大厂资料。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

…(img-HbsqwdFO-1711650166855)]

总结

在清楚了各个大厂的面试重点之后,就能很好的提高你刷题以及面试准备的效率,接下来小编也为大家准备了最新的互联网大厂资料。

[外链图片转存中…(img-3YmKrHDX-1711650166855)]

[外链图片转存中…(img-EP8ArjRX-1711650166856)]

[外链图片转存中…(img-xmhZdxIc-1711650166856)]

[外链图片转存中…(img-rO3cT0vI-1711650166857)]

  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值