java面试常见的并发问题篇二(吊打面试官)

java面试常见的并发问题篇二(吊打面试官)

提示:要想生活过得好 榜上富婆要趁早



一、JMM(Java内存模型)

1.说说对Java内存模型的理解

JMM(Java 内存模型)主要定义了对于一个共享变量,当另一个线程对这个共享变量执行写操作后,这个线程对这个共享变量的可见性,是一种抽象的模型,被定义出来屏蔽各种硬件和操作系统的内存访问差异。
Java内存模型的目的是为了简化多线程编程,增强程序可移植性

CPU 缓存:为了解决 CPU 处理速度和内存处理速度不对等的问题。
内存缓存:硬盘数据用于解决硬盘访问速度过慢的问题。

二、volatile关键字

1.如何保证变量的可见性?

在 Java 中,volatile 关键字可以保证变量的可见性,如果我们将变量声明为 volatile ,这就指示 JVM,这个变量是共享且不稳定的,每次使用它都到主存中进行读取。

注:volatile 关键字能保证数据的可见性,但不能保证数据的原子性。synchronized 关键字两者都能保证。


2.如何禁止指令重排序?

在 Java 中,volatile 关键字除了可以保证变量的可见性,还有一个重要的作用就是防止 JVM 的指令重排序。

3.volatile可以保证原子性吗?

原子性:即一个操作或者多个操作,要么全部执行,并且执行的过程不会被任何因素打断,要么就都不执行。

volatile 关键字能保证变量的可见性,但不能保证对变量的操作是原子性的。


三、乐观锁和悲观锁

1.什么是悲观锁?

悲观锁:总是假定最坏的情况,总是假定共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作的时候都会上锁,这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放。也就是说,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程

像 Java 中synchronizedReentrantLock等独占锁就是悲观锁思想的实现。

因为悲观锁为导致线程堵塞,所以在高并发场景中,可能会出现大量堵塞线程,导致系统的上下文切换,还可能会导致死锁问题。

2.什么是乐观锁?

相反,乐观锁总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只是在提交修改的时候去验证对应的数据是否被其它线程修改了。

高并发的场景下,乐观锁相比悲观锁来说,不存在锁竞争造成线程阻塞,也不会有死锁的问题,在性能上会更高,但是,如果冲突频繁发生,会频繁失败和重试,这样同样会非常影响性能,导致 CPU 飙升。

3.如何实现乐观锁?

乐观锁一般会使用版本号机制 CAS 算法实现,cas算法相对用得比较多。

版本号机制:一般是在数据表中加上一个数据版本号 version 字段,表示数据被修改的 次数。当数据被修改时,version 值会加一。当线程 A 要更新数据值时,在读取数据的同时也会读取 version 值,在提交更新时,若刚才读取到的 version 值为当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功。

CAS 算法: 全称Compare And Swap ,CAS 的思想很简单,就是用一个预期值和要更新的变量值进行比较,两值相等才会进行更新。
注:CAS 是一个原子操作

4.乐观锁存在哪些问题?

1.ABA问题(常见)
如果一个变量 V 初次读取的时候是 A 值,并且在准备赋值的时候检查到它仍然是 A 值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回 A,那 CAS 操作就会误认为它从来没有被修改过。这个问题被称为 CAS 操作的 "ABA"问题。
解决方案:ABA 问题的解决思路是在变量前面追加上版本号或者时间戳。

2.循环时间长开销大。
CAS 经常会用到自旋操作来进行重试,也就是不成功就一直循环执行直到成功。如果长时间不成功,会给 CPU 带来非常大的执行开销。

3.只能保证一个共享变量的原子操作
注:CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。
但是,可以把多个共享变量合并成一个共享变量,放在一个对象中使用。


synchronized关键字

1.什么是 synchronized?

synchronized中文翻译是同步的意思,主要解决多个线程之间访问资源的同步性,被他修饰的方法或者代码块,在任意时刻只允许一个线程执行任务。

2.如何使用synchronized?

*主要有三种方法:

  • 1.修饰实例方法
    给当前实例对象加上锁,进入到同步代码前要先获得实例对象的锁
  • 2.修饰静态方法
    给当前类加锁,进入到同步代码前要先获得当前class的锁, 因为静态成员属于类所有,不属于实例对象。
  • 3.修饰代码块
synchronized(this) {
    //业务代码
}

3.说说synchronized的底层原理?

1.synchronized修饰代码块时,JVM采用monitorentermonitorexit两个指令来实现同步,monitorenter 指令指向同步代码块的开始位置, monitorexit 指令则指向同步代码块的结束位置
2.synchronized修饰同步方法时,JVM采用ACC_SYNCHRONIZED标记符来实现同步,这个标识指明了该方法是一个同步方法。

monitorenter、monitorexit或者ACC_SYNCHRONIZED都是基于Monitor实现的,所谓的Monitor其实是一种同步工具,也可以说是一种同步机制。

4.锁升级?synchronized优化了解吗?

在JDK1.6之后,synchronized 引入了大量的优化如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销,这些优化让 synchronized 锁的效率提升了很多。
锁升级方向:无锁–>偏向锁—> 轻量级锁---->重量级锁,这个方向基本上是不可逆的。

5.synchronized 关键字和 volatile 关键字的区别

注意:两者是互补的存在而非对立的存在
1.volatile 关键字是线程同步的轻量级实现, volatile性能比synchronized关键字好 ,但是 volatile 关键字只能用于变量而 synchronized 关键字可以修饰方法以及代码块 。
2.volatile 关键字能保证数据的可见性,但不能保证数据的原子性。synchronized 关键字两者都能保证。
3.volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized 关键字解决的是多个线程之间访问资源的同步性

四、ReentrantLock

1.什么是ReentrantLock?

ReentrantLock 是可重入的独占锁,只能有一个线程可以获取该锁,其它获取该锁的线程会被阻塞而被放入该锁的阻塞队列里面,和 synchronized 关键字类似。不过,ReentrantLock增加了轮询、超时、中断、公平锁和非公平锁等高级功能。

2.公平锁和非公平锁

公平锁 : 锁被释放之后,先申请的线程先得到锁。性能较差一些,因为公平锁为了保证时间上的绝对顺序,上下文切换更频繁。

非公平锁:锁被释放之后,后申请的线程可能会先获取到锁,是随机或者按照其他优先级排序的。性能更好,但可能会导致某些线程永远无法获取到锁。

3.synchronized和ReentrantLock的区别

相同:1.两者都是可重入锁。
不同:1.synchronized 依赖于JVM 而 ReentrantLock 依赖于API
2.ReentrantLock 比 synchronized 增加了一些高级功能,主要有三点:

  • 等待可中断: ReentrantLock提供了一种能够中断等待锁的线程的机制,也就是说正在等待的线程可以选择放弃等待,改为处理其他事情.
  • 可实现公平锁
  • 实现选择性通知(锁可以绑定多个条件):synchronized关键字与wait()和notify()/notifyAll()方法相结合可以实现等待/通知机制。ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition()方法。

总结

以上就是本篇的全部内容,以下3点内容说明:
0.以上内容来源于javaguide和网页搜索得来,博主本人对其润色修改以供偏于自己背读。
1.这篇是并发的二篇,还有三篇。
2.如果有好的八股文可以分享在评论区,大家一起学习

好了,希望大家能够在面试中顺利通过,并吊打面试官。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值