多线程(锁升级, 锁消除, 锁粗化)

简介: 多线程(锁升级, 锁消除, 锁粗化)

锁升级

JVM 将 synchronized 锁分为四个状态: 无锁, 偏向锁, 轻量级锁, 重量级锁.

JVM 会根据清空, 自动进行锁升级.


偏向锁

偏向锁不是真的 “加锁”, 只是给锁住的资源做了一个标记, 记录这个锁属于哪个线程.

如果后续没有其他线程来竞争锁, 那么就不用进行其他操作 (减少了加锁解锁的开销)

如果后续有其他线程来竞争锁, 就会根据标记, 取消 偏向锁状态, 进入轻量级锁状态

轻量级锁

轻量级锁即自适应的自旋锁

synchronized 的轻量级锁是通过 CAS 来实现

  • 通过 CAS 检查并更新一块内存
  • 如果更新成功, 则认为加锁成功
  • 如果更新失败, 则认为锁被占用, 继续自旋式等待(并不放弃 CPU, 以便解锁后能更快获得锁)

自适应的含义

自旋锁是一直让 CPU 空转, 比较浪费 CPU 资源

自适应的意思是 : 自旋不会一直持续进行, 到达一定的时间/重试次数, 就停止自旋(放弃 CPU 等待)


重量级锁

自旋锁经过一定时间/重试次数, 依旧不能获取锁, 就会锁升级为重量级锁

此处的重量级锁就是指用到内核提供的 mutex.

  • 执行加锁操作, 先进入内核态
  • 在内核态判定当前锁是否已被占用
  • 如果锁未被占用, 则加锁成功, 并切换回用户态
  • 如果该锁被占用, 则加锁失败. 此时线程进入锁的等待队列, 挂起. 等待 操作系统 唤醒
  • 当这个锁被其他线程释放, 操作系统唤醒等待队列中挂起的线程, 该线程尝试获取锁

锁消除

编译器 和 JVM 对于代码的优化操作

如果编译器 + JVM 判定你的代码中加的锁没有用到 (比如单线程环境下的加锁操作), 就会自动消除锁

eg :

StringBuffer sb = new StringBuffer();
sb.append("a");
sb.append("b");
sb.append("c");
sb.append("d");

StringBuffer.append() 涉及加锁解锁操作, 但是如果在单线程环境下运行此代码, 那么这些加锁解锁操作就会浪费不必要的资源, 就会被自动消除掉(编译阶段)


锁粗化

锁的粒度 : 单个锁所保护资源的数据量, 我们以 来表示

锁粗化就是: 一段逻辑中多次出现加锁解锁操作, 编译器 + JVM 就会自动将这些操作合并为一个加锁解锁操作 (加锁和解锁操作涉及资源的消耗)

下面代码就是把50000次加锁解锁操作合并为一个加锁解锁操作, 节省了很多资源消耗


目录
相关文章
|
24天前
|
安全 Java 编译器
线程安全问题和锁
本文详细介绍了线程的状态及其转换,包括新建、就绪、等待、超时等待、阻塞和终止状态,并通过示例说明了各状态的特点。接着,文章深入探讨了线程安全问题,分析了多线程环境下变量修改引发的数据异常,并通过使用 `synchronized` 关键字和 `volatile` 解决内存可见性问题。最后,文章讲解了锁的概念,包括同步代码块、同步方法以及 `Lock` 接口,并讨论了死锁现象及其产生的原因与解决方案。
56 10
线程安全问题和锁
|
5天前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
17 2
|
19天前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
2月前
|
数据采集 存储 安全
如何确保Python Queue的线程和进程安全性:使用锁的技巧
本文探讨了在Python爬虫技术中使用锁来保障Queue(队列)的线程和进程安全性。通过分析`queue.Queue`及`multiprocessing.Queue`的基本线程与进程安全特性,文章指出在特定场景下使用锁的重要性。文中还提供了一个综合示例,该示例利用亿牛云爬虫代理服务、多线程技术和锁机制,实现了高效且安全的网页数据采集流程。示例涵盖了代理IP、User-Agent和Cookie的设置,以及如何使用BeautifulSoup解析HTML内容并将其保存为文档。通过这种方式,不仅提高了数据采集效率,还有效避免了并发环境下的数据竞争问题。
如何确保Python Queue的线程和进程安全性:使用锁的技巧
|
8天前
|
存储 算法 Java
关于python3的一些理解(装饰器、垃圾回收、进程线程协程、全局解释器锁等)
该文章深入探讨了Python3中的多个重要概念,包括装饰器的工作原理、垃圾回收机制、进程与线程的区别及全局解释器锁(GIL)的影响等,并提供了详细的解释与示例代码。
15 0
|
2月前
|
Java 开发者
Java多线程教程:使用ReentrantLock实现高级锁功能
Java多线程教程:使用ReentrantLock实现高级锁功能
34 1
|
1月前
|
安全 Java API
Java线程池原理与锁机制分析
综上所述,Java线程池和锁机制是并发编程中极其重要的两个部分。线程池主要用于管理线程的生命周期和执行并发任务,而锁机制则用于保障线程安全和防止数据的并发错误。它们深入地结合在一起,成为Java高效并发编程实践中的关键要素。
15 0
|
2月前
|
数据采集 Java Python
python 递归锁、信号量、事件、线程队列、进程池和线程池、回调函数、定时器
python 递归锁、信号量、事件、线程队列、进程池和线程池、回调函数、定时器
|
2月前
|
Java 开发者
解锁Java并发编程的秘密武器!揭秘AQS,让你的代码从此告别‘锁’事烦恼,多线程同步不再是梦!
【8月更文挑战第25天】AbstractQueuedSynchronizer(AQS)是Java并发包中的核心组件,作为多种同步工具类(如ReentrantLock和CountDownLatch等)的基础。AQS通过维护一个表示同步状态的`state`变量和一个FIFO线程等待队列,提供了一种高效灵活的同步机制。它支持独占式和共享式两种资源访问模式。内部使用CLH锁队列管理等待线程,当线程尝试获取已持有的锁时,会被放入队列并阻塞,直至锁被释放。AQS的巧妙设计极大地丰富了Java并发编程的能力。
33 0
|
2月前
|
缓存 Java
【多线程面试题二十三】、 说说你对读写锁的了解volatile关键字有什么用?
这篇文章讨论了Java中的`volatile`关键字,解释了它如何保证变量的可见性和禁止指令重排,以及它不能保证复合操作的原子性。