Java并发编程面试题: 并发理论(锁升级、synchronized & Lock、volatile关键字 )

收集大量Java经典面试题目📚,内容涵盖了包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 等知识点🏝️。适合准备Java面试的读者参考和复习🌟📢。

❗ ❗ ❗ 关注公众号:枫蜜柚子茶 ✅✅🗳
📑 回 复 “ Java面试 ” 获 取 完 整 资 料⬇ ⬇ ⬇

📖Java并发编程面试Top123道题🔥🔥
1️⃣ 基 础 知 识 
2️⃣ 并 发 理 论 🚩
3️⃣ 线 程 池  
4️⃣ 并 发 容 器

5️⃣ 并 发 队 列

6️⃣ 并 发 工 具 类

1. 多线程中 synchronized 锁升级的原理是什么?

        synchronized 锁升级原理:在锁对象的对象头里面有一个 threadid 字段,在第一次访问的时候     threadid 为空,jvm 让其持有偏向锁,并将 threadid 设置为其线程 id,再次进入的时候会先判断  threadid 是否与其线程 id 一致,如果一致则可以直接使用此对象,如果不一致,则升级偏向锁为  轻量级锁,通过自旋循环一定次数来获取锁,执行一定次数之后,如果还没有正常获取到要使用的 对象,此时就会把锁从轻量级升级为重量级锁,此过程就构成了 synchronized 锁的升级。

        🔐锁的升级的目的:锁升级是为了减低了锁带来的性能消耗。在 Java 6 之后优化 synchronized 的实现方式,使用了偏向锁升级为轻量级锁再升级到重量级锁的方式,从而减低了锁带来的性能消耗。

​​​​​​​

        偏向锁,顾名思义,它会偏向于第一个访问锁的线程,如果在运行过程中,同步锁只有一个线程访问,不存在多线程争用的情况,则线程是不需要触发同步的,减少加锁/解锁的一些CAS操作(比如等待队列的一些CAS操作),这种情况下,就会给线程加一个偏向锁。 如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,  JVM会消除它身上的偏向锁,将锁恢复到标准的轻量级锁。

        轻量级锁是由偏向所升级来的,偏向锁运行在一个线程进入同步块的情况下,当第二个线程加入锁 争用的时候,轻量级锁就会升级为重量级锁;

        重量级锁是synchronized ,是 Java 虚拟机中最为基础的锁实现。在这种状态下, Java 虚拟机会阻 塞加锁失败的线程,并且在目标锁被释放的时候,唤醒这些线程。

        公平锁,公平锁是指多个线程按照它们发出请求的顺序来获取锁,即先来先得。当一个线程释放锁时,等待队列中的线程会按照它们进入队列的顺序逐个获得锁。这种方式可以确保所有线程都有机会获取锁,避免某些线程一直被饿死(永远无法获取锁)的情况。 

2. 线程 B 怎么知道线程 A 修改了变量

  • 1volatile 修饰变量
  • 2synchronized 修饰修改变量的方法 .  
  • 3wait/notify
  • 4while 轮询

3. 当一个线程进入一个对象的 synchronized 方法 A 之后,其它线程是否可进入此对象的 synchronized 方法 B

​​​​​​​

        ❌ 不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的synchronized 修饰符要求执行方法时要获得对象的锁,如果已经进入A 方法说明对象锁已经被取 走,那么试图进入 B 方法的线程就只能在等锁池(注意不是等待池哦)中等待对象的锁。

4. synchronizedvolatileCAS 比较

1synchronized 是悲观锁,属于抢占式,会引起其他线程阻塞。

2volatile 提供多线程共享变量可见性和禁止指令重排序优化。    

3CAS 是基于冲突检测的乐观锁(非阻塞)

5. synchronized Lock 有什么区别?​​​​​​​

  • ◾  首先synchronizedJava内置关键字,在JVM层面, Lock是个Java类;
  • ◾  synchronized可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。
  • ◾  synchronized不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁; lock 需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁。
  • ◾  通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。

6. synchronized ReentrantLock 区别是什么?

        synchronized 是和 ifelseforwhile 一样的关键字, ReentrantLock 是类,这是二者的本质区别。既然 ReentrantLock 是类,那么它就提供了比synchronized 更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量。

        synchronized 早期的实现比较低效,对比 ReentrantLock,大多数场景性能都相差较大,但是在 Java 6 中对 synchronized 进行了非常多的改进。

✅​​​​​​​相同点:两者都是可重入锁

        两者都是可重入锁。 可重入锁概念是:自己可以再次获取自己的内部锁。比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不 可锁重入的话,就会造成死锁。同一个线程每次获取锁,锁的计数器都自增1,所以要等到锁的计数器  下降为0时才能释放锁。

📛主要区别如下:

  • ◾   ReentrantLock 使用起来比较灵活,但是必须有释放锁的配合动作;
  • ◾   ReentrantLock 必须手动获取与释放锁,而 synchronized不需要手动释放和开启锁; o   ReentrantLock 只适用于代码块锁,而 synchronized可以修饰类、方法、变量等。
  • ◾   二者的锁机制其实也是不一样的。 ReentrantLock 底层调用的是 Unsafe park 方法加锁, synchronized 操作的应该是对象头中 mark word
  • ◾  Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:
  • ◾   普通同步方法,锁是当前实例对象
  • ◾   静态同步方法,锁是当前类的class对象 。    同步方法块,锁是括号里面的对象

7. volatile 关键字的作用

        对于可见性,Java 提供了 volatile 关键字来保证可见性和禁止指令重排。  volatile 提供 happens- before 的保证,确保一个线程的修改能对其他线程是可见的。当一个共享变量被 volatile 修饰时,它会保证修改的值会立即被更新到主内存中,当有其他线程需要读取时,它会去内存中读取新值。

        从实践角度而言, volatile 的一个重要作用就是和 CAS 结合,保证了原子性,详细的可以参见 java.util.concurrent.atomic 包下的类,比如 AtomicInteger

        volatile 常用于多线程环境下的单次操作(单次读或者单次)

8. Java 中能创建 volatile 数组吗?

        能,Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用,而不是整个数组。意思 是,如果改变引用指向的数组,将会受到 volatile 的保护,但是如果多个线程同时改变数组的元 素,volatile 标示符就不能起到之前的保护作用了。

9. volatile 变量和 atomic 变量有什么不同?

        ◾  volatile 变量可以确保先行关系,即写操作会发生在后续的读操作之前, 但它并不能保证原子性。 例如用 volatile 修饰 count 变量,那么 count++ 操作就不是原子性的。

        ◾  而 AtomicInteger 类提供的 atomic 方法可以让这种操作具有原子性如getAndIncrement()方法会 原子性的进行增量操作把当前值加一 ,其它数据类型和引用变量也可以进行相似操作。

10. volatile 能使得一个非原子操作变成原子操作吗?

        ◾  关键字volatile的主要作用是使变量在多个线程间可见,但无法保证原子性,对于多个线程访问同 一个实例变量需要加锁进行同步。

        ◾  虽然volatile只能保证可见性不能保证原子性,但用volatile修饰longdouble可以保证其操作原子性。

🔻所以从Oracle Java Spec里面可以看到:

        ◾  对于64位的longdouble,如果没有被volatile修饰,那么对其操作可以不是原子的。在操作的时候,可以分成两步,每次对32位操作。

        ◾  如果使用volatile修饰longdouble,那么其读写都是原子操作 。

        ◾  对于64位的引用地址的读写,都是原子操作

        ◾  在实现JVM时,可以自由选择是否把读写longdouble作为原子操作 

✅​​​​​​​ 推荐JVM实现为原子操作

11. synchronized volatile 的区别是什么?

⭕ synchronized 表示只有一个线程可以获取作用对象的锁,执行代码,阻塞其他线程。

⭕  volatile 表示变量在 CPU 的寄存器中是不确定的,必须从主存中读取。保证多线程环境下变量的 可见性;禁止指令重排序。

🔰区别:

  • ◾  volatile 是变量修饰符; synchronized 可以修饰类、方法、变量。
  • ◾  volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改 可见性和原子性。
  • ◾  volatile 不会造成线程的阻塞; synchronized可能会造成线程的阻塞。
  • ◾  volatile标记的变量不会被编译器优化; synchronized标记的变量可以被编译器优化。​​​​​​​
  • ◾  volatile关键字是线程同步的轻量级实现,所以volatile性能肯定比synchronized关键字要好。但是 volatile关键字只能用于变量而synchronized关键字可以修饰方法以及代码块。 synchronized关键 字在JavaSE1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻  量级锁以及其它各种优化之后执行效率有了显著提升,实际开发中使用 synchronized 关键字的场 景还是更多一些。

12. final不可变对象,它对写并发应用有什么帮助?

        不可变对象(Immutable Objects) 即对象一旦被创建它的状态(对象的数据,也即对象属性值)就 不能改变,反之即为可变对象(Mutable Objects)

        不可变对象的类即为不可变类(Immutable Class)。Java 平台类库中包含许多不可变类,如 String、基本类型的包装类、 BigInteger BigDecimal 等。

只有满足如下状态, 一个对象才是不可变的;

  •         ◾  它的状态不能在创建后再被修改;
    •         ◾   所有域都是 final 类型;并且,它被正确创建(创建期间没有发生this 引用的逸出)。

不可变对象保证了对象的内存可见性,对不可变对象的读取不需要进行额外的同步手段,提升了代码执行效率。

13. Lock 接口和synchronized 对比同步它有什么优势?

        Lock 接口比同步方法和同步块提供了更具扩展性的锁操作。他们允许更灵活的结构,可以具有完全不同的性质,并且可以支持多个相关类的条件对象。 

✅它的优势有:

  • 1)可以使锁更公平。
  • 2)可以使线程在等待锁的时候响应中断。
    • 3)可以让线程尝试获取锁,并在无法获取锁的时候立即返回或者等待一段时间。
      • 4)可以在不同的范围,以不同的顺序获取和释放锁。

        整体上来说 Lock synchronized 的扩展版, Lock 提供了无条件的、可轮询的(tryLock 方法)、定时的(tryLock 带参方法)、可中断的(lockInterruptibly)、可多条件队列的(newCondition 方法)锁操作。另外 Lock 的实现类基本都支持非公平锁(默认)和公平锁, synchronized 只支持非公平锁,当然,在大部分情况下,非公平锁是高效的选择。​​​​​​​

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

枫蜜柚子茶

你的鼓励是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值