Java面试题-锁机制

1. 说说线程安全问题

多线程并发下,产生安全问题要满足以下三个条件:

  • 多线程并发
  • 共享数据
  • 共享数据修改

为什么会有多线程下,线程安全的问题?

这里就涉及到Java虚拟机(简称JVM)的相关知识了。JVM启动后,本地内存分配给JVM一定的内存空间,称为“主内存”;而线程间又有各自独立的“工作内存”,工作内存中的数据来源于主内存数据的拷贝。正因为这个拷贝工作内存的数据它不是原版数据,工作内存可能被其他线程所修改,这时候就有数据不一致的问题。

开发中,如何解决线程安全问题?

核心思想:

  • 加锁
  • 共享数据调整为:局部变量、不可变对象、ThreadLocal封装共享数据、CAS进行共享数据修改、volatile修饰(只保证可见性、禁止指令重排,不保证原子性)

Java线程安全问题理解

2. volatile实现原理

volatile修饰的变量具有如下特性:

  • 可见性
  • 有序性(禁止指令重排)

什么是可见性?

可见性:多个线程访问同一个共享变量时,其中一个线程对这个共享变量值的修改,其他线程能够立刻获得修改以后的值

什么是有序性?

有序性:编译器和处理器为了优化程序性能而对指令序列进行重排序,也就是你编写的代码顺序和最终执行的指令顺序是不一致的。

但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。

volatile如何保证可见性?

总结为一句话:通过内存屏障禁止指令重排保证可见性。

具体分析如下:

JMM内存模型下,数据操作通过如下8种方式:

  • 1、read(读取):从主内存读取数据
  • 2、load(载入):将主内存的数据写入工作内存
  • 3、use(使用):从工作内存读取数据计算
  • 4、assign(赋值):将计算好的值重新赋值到工作内存中
  • 5、store(存储):将工作内存的值写入主内存
  • 6、write(写入):将store过去的变量值赋值给主内存中的变量
  • 7、lock(锁定):将主内存变量枷锁,标识未线程独占状态
  • 8、unlock(解锁):将主内存变量解锁,解锁后其他线程可以锁定该变量

volatile修饰的变量在写入操作时,在写入后,加入5、store(存储)内存屏障。(写入后,将工作内存中的数据写入主内存

volatile修饰的变量在读取操作时,在读取前,加入2、load(载入)内存屏障。(读取前,从主内存中读取数据刷到工作内存

如上一来,volatile读取的数据是主内存中数据,一定是最新的,因此能够保证可见性。

volatile如何保证可见性

3. synchronized实现原理

synchronized可以用来修饰 代码块方法

  • 修饰方法

    同步方法的常量池有ACC_SYNCHRONIZED标识,线程访问方法时会判断是否有ACC_SYNCHRONIZED标识。

    如果有,则先获取监视器锁,然后执行方法,最后释放监视器锁。

    如果有,且获取监视器锁失败,则会阻塞,直到监视器锁被其他线程释放后,获取到监视器锁。

    在发生异常的情况且没有处理异常,则在异常抛出前会释放监视器锁。

  • 修饰代码块

​ 修饰代码块时,底层会加入monitorentermonitorexit指令。

每个对象都有一个锁次数的计数器,未被锁的计数器为0.

​ 当线程进入代码块时,执行monitorenter指令,锁次数计数器 +1,当该线程再次获取锁时,则再次+1。

​ 当执行monitorexit释放锁时,锁次数计数器-1。

​ 直到锁次数计数器为0时,锁被释放,此时其他线程能够得到该代码块的锁。

4. synchronized和lock的区别

1、synchronized是关键字,lock是接口。

2、synchronized是隐式加锁,lock是API层面的加锁。

3、synchronized可以作用在方法和代码块,lock只能作用在代码块。

4、synchronized是阻塞式加锁,lock支持非阻塞式加锁。

5、synchronized没有超时机制,lock中的tryLock支持超时机制。

6、synchronized不可中断,lock中的lockInterruptibly()中断获取的锁。

7、synchronized底层采用monitorlock底层采用AQS。

8、synchronized是非公平锁;lock支持非公平锁与公平锁。

9、synchronized唤醒方式是notify/notifyAlllock唤醒方式是condition

5. CAS乐观锁

CAS:Compare AND Swap,比较并交换。是一种通过乐观锁保证变量操作原子性的机制。

该方法包含3个操作数:内存位置预期原值新值,先通过 内存位置定位变量,比较 预期原值与当前 内存位置中的值是否一致;一致则会进行修改;不一致则不做如何操作。

优点

资源竞争不大的情况下,系统开销小。

缺点

1、ABA问题

2、竞争大的情况下,CPU开销大

6. ABA问题

主线程通过CAS在取出指定 内存位置的值后,比较 预期原值并修改操作之前,有一定的时间差;在这个时间差内,其他线程将A修改为B,又将B修改为A,此时主线程并不会发觉内存位置的值已被修改过,会继续进行 比较预期原值并修改操作

尽管主线程的CAS操作完成,但这个过程是有风险的。

解决方案:

加上版本号解决ABA问题:只有版本号一致的情况下才能进行修改操作,每次修改操作都对 版本号执行+1操作。

7. 乐观锁的业务场景及实现方式

适用场景为:冲突不频繁的业务下。

现有的数据库中,对于多数表会增加一个version字段,该字段能够有效避免数据修改的ABA问题。

猜你喜欢

转载自blog.csdn.net/qq_36986510/article/details/129438946