JavaDriver JavaDriver
首页
  • 基础
  • 并发
  • JVM
  • 设计模式
  • 计算机网络
  • 操作系统
  • 数据结构
  • 算法
  • MYSQL
  • REDIS
  • Netty
  • Kafka
系统设计
非技术
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

YoungAnn

西二旗Java老司机一枚 致力于社会主义添砖Java
首页
  • 基础
  • 并发
  • JVM
  • 设计模式
  • 计算机网络
  • 操作系统
  • 数据结构
  • 算法
  • MYSQL
  • REDIS
  • Netty
  • Kafka
系统设计
非技术
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础

  • 并发

    • 线程池是如何实现的?
    • 简述 CAS 原理,什么是 ABA 问题,怎么解决?
    • 简述 Synchronized,Volatile,可重入锁的不同使用场景及优缺点
      • Java中的内存可见性
      • Volatile 的实现原理
      • Synchronized 的用法
      • Synchronized 的锁存储在哪里?
      • 为什么Java中每个对象都可以作为锁?
      • Synchronized 锁升级
        • 偏向锁
        • 轻量级锁
        • 重量级锁
        • 锁升级
        • 锁的优缺点对比
      • Java如何实现原子操作
        • CAS实现原子操作
        • CAS 存在的三个问题
        • 锁机制实现原子操作
    • Synchronized 与 Lock 相比优缺点分别是什么?
    • 重入锁是如何实现的?
    • volatile 关键字解决了什么问题,它的实现原理是什么?
    • 简述 Java 锁升级的机制
    • 简述 Java AQS 的原理以及使用场景
    • 什么是公平锁?什么是非公平锁?
    • Java 的线程有哪些状态,转换关系是怎么样的?
    • Java 是如何实现线程安全的,哪些数据结构是线程安全的?
    • 手写死锁
    • 为什么我们不能直接调用 run() 方法?
    • Java 线程有哪些常用方法?
    • 手写生产者消费者模型
    • ThreadLocal 实现原理是什么?为什么要使用弱引用?
  • JVM

  • 设计模式

  • Java相关
  • 并发
YoungAnn
2022-04-04
目录

简述 Synchronized,Volatile,可重入锁的不同使用场景及优缺点

在多线程编程中 Synchronized,Volatile 都扮演着重要角色, 都已用来实现原子操作。 Volatile 是轻量级的 Synchronized ,保证了共享变量的可见性。

可见性的意思是:当线程A修改共享变量的值后,线程B能立刻读到这个修改后的值。

Volatile 不会引起线程上下文的切换和调度,如果使用的恰当,会比 Synchronized 执行成本更低。

# Java中的内存可见性

  • 可见性:一个线程对共享变量值的修改,能够及时被其他线程看到。

  • 共享变量:如果一个变量在多个线程的工作内存中都存在副本,那这个变量就是这几个线程的共享变量。

  • Java内存的规定:

-线程对共享变量的所有操作都必须在自己的工作内存中进行,不可直接从主内存中读写; -不同线程之间无法直接访问其他线程工作内存中的变量,线程间的变量值的传递需要通过主内存。

# Volatile 的实现原理

如果对用 Volatile 修饰的变量写操作,JVM 会向处理器发出一条 Lock 前缀的指令,Lock 前缀的指令在多核处理器下会引发两件事情:

  1. 将当前处理器缓存行的数据写会到系统内存

  2. 这个写会内存的操作会使其他缓存中的该内存地址的数据无效

# Synchronized 的用法

  1. 普通同步方法:锁当前实例对象

public synchronized void method() {

}

1
2
3
4
5
  1. 静态同步方法:锁当前类的Class对象

public synchronized static void method() {

}

1
2
3
4
5
  1. 同步代码块:锁括号里对象

public void method()

{

synchronized(this) {

}

}

1
2
3
4
5
6
7
8
9
10
11

# Synchronized 的锁存储在哪里?

Synchronized的锁存储在Java的对象头里。

Java 对象头里的 Mark Word 用于存储对象的 HashCode 、分代年龄和锁标记位。

32位虚拟机中, Mark Word 的存储结构如下:

在运行期, Mark Word 有四种状态:轻量级锁、重量级锁、GC 标记、偏向锁,各状态下的存储结构如下图:

在64位虚拟机下,Mark Word 的存储结构如下图:

# 为什么Java中每个对象都可以作为锁?

任何对象都有一个 monitor 与之关联,当一个 monitor 被持有后,他将处于锁定状态。monitor 是用 C++ 实现的。

同步语句块的实现使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。

monitorenter 指令是在编译后插入到同步代码块的开始位置,而 monitorexit 是插入到同步块的结束处和异常处,JVM 要保证每个 monitorenter 都有 monitorexit 与之配对。

# Synchronized 锁升级

Synchronized 一直被称为重量级锁。但是在JDK 1.6之后它已经变得不那么重了。JDK 1.6 对Synchronized 的优化点在于:

  • 引入了偏向锁

  • 引入了轻量级锁

在JDK 1.6 中,Synchronized 锁有四种状态,级别从低到高依次是:无锁、偏向锁、轻量级锁、重量级锁。

这几个状态会随着锁竞争升级,但是不可以降级。

# 偏向锁

为什么引入偏向锁?

不存锁竞争,或者总是由同一线程多次获得锁的场景,偏向锁的代价更低。

当一个线程访问同步块并获取到锁时,在锁对象头记录该线程的id,以后该线程进入和退出该同步块时不需要CAS来加锁和解锁。

偏向锁何时释放?

偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程是不会主动释放偏向锁的。

偏向锁一定起到正面作用吗?

不是的。

偏向锁的适用场景是:不存锁竞争,或者总是由同一线程多次获得锁的场景。

如果你确定你的程序中 锁通常处于竞争状态,可以通过JVM参数关闭偏向锁。关闭后,程序回魔人进入轻量级锁状态。


-XX:UseBiasedLocking=false

1
2
3

# 轻量级锁

轻量级锁是指当锁是偏向锁的时候,却被另外的线程所访问,此时偏向锁就会升级为轻量级锁,其他线程会通过自旋(关于自旋的介绍见文末)的形式尝试获取锁,线程不会阻塞,从而提高性能。

轻量级锁的获取主要由两种情况:① 当关闭偏向锁功能时;② 由于多个线程竞争偏向锁导致偏向锁升级为轻量级锁。

轻量级锁何时升级为重量级锁?

若当前只有一个等待线程,则该线程将通过自旋进行等待。但是当自旋超过一定的次数时,轻量级锁便会升级为重量级锁(锁膨胀)。

另外,当一个线程已持有锁,另一个线程在自旋,而此时又有第三个线程来访时,轻量级锁也会升级为重量级锁(锁膨胀)。

# 重量级锁

重量级锁是指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。

重量级锁的获取是释放一般会有线程上下文切换,代价是比较大的,所以说是重量级锁。

# 锁升级

# 锁的优缺点对比

# Java如何实现原子操作

原子操作:不可被中断的一个或一系列操作。

Java 有两种实现原子操作的方式:CAS(compare and swap)、锁。

# CAS实现原子操作

CAS理论是 juc 包实现的基石,在intel的CPU中,CAS 通过调用本地方法(JNI)使用cmpxchg指令来实现的非阻塞算法。对比于synchronized阻塞算法,基于 CAS 实现的 juc 在性能上有了很大的提升。

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。

# CAS 存在的三个问题

  1. ABA问题
  • 因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。

  • ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。

  • 从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

  1. 循环时间长开销大

  2. 只能保证一个共享变量的原子操作

从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作。

# 锁机制实现原子操作

锁机制保证了只有获得锁的线程才能操作指定的内存区域。除了偏向锁,JVM实现锁的方式都使用了循环CAS。

编辑 (opens new window)
上次更新: 2022/05/19, 21:26:01
简述 CAS 原理,什么是 ABA 问题,怎么解决?
Synchronized 与 Lock 相比优缺点分别是什么?

← 简述 CAS 原理,什么是 ABA 问题,怎么解决? Synchronized 与 Lock 相比优缺点分别是什么?→

最近更新
01
电商-商品系统设计
12-17
02
关于如何写OKR
12-09
03
对事不对人 vs 对人不对事
12-09
更多文章>
Theme by Vdoing | Copyright © 2022-2023 YoungAnnn | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式