多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

多线程(进阶)主要学习思想

多线程(进阶)主要学习思想 Java多线程锁策略 笔记通俗易懂例子这三组是不同维度对锁的分类不要混在一起悲观锁 / 乐观锁思想层面认为会不会发生并发冲突重量级锁 / 轻量级锁synchronized底层实现层面JVM里对象头的锁状态挂起等待锁 / 自旋锁等待锁失败时的行为拿不到锁怎么办一、悲观锁 VS 乐观锁1. 悲观锁想法默认一定会发生并发冲突每次操作数据先上锁保护。只要操作共享数据先把锁拿到手别人无法修改。等我操作完释放锁其他人才能访问。 ✅ 典型例子synchronized、ReentrantLock、数据库select ... for update生活例子去卫生间进门直接反锁。默认认为一定会有人跟我抢位置。特点冲突多的时候性能更好会阻塞其他线程拿不到锁就等待2. 乐观锁想法默认不会发生并发冲突不加锁提交修改的时候检查有没有被别人改动过。不上锁直接读写更新时校验版本号。如果数据已经被别人改了本次修改失败重试。 ✅ 典型例子volatile CASAtomicInteger、数据库版本号version机制生活例子图书馆自习座位不锁门当你要坐下前看一眼座位有没有被占。被占了就换位置重试。特点冲突少的时候性能高没有阻塞失败就重试冲突特别多的时候会不断重试CPU浪费严重一句话区分悲观锁先锁再干活乐观锁先干活提交时检查冲突。二、重量级锁 VS 轻量级锁特指synchronized底层这两个都是悲观锁是JVM对synchronized的优化存储在对象头Mark Word。1. 重量级锁依赖操作系统内核的互斥锁。 线程拿不到锁 →操作系统把线程挂起放到内核等待队列锁释放后操作系统再唤醒线程。缺点线程挂起阻塞、唤醒需要用户态↔内核态切换开销很大。例子多个线程长时间竞争同一个synchronized锁锁升级成重量级锁。2. 轻量级锁用户态实现不调用操作系统内核锁。 适用场景多个线程交替使用锁几乎不并发竞争。 拿锁失败不会直接挂起而是尝试自旋。注意一旦出现两条线程同时抢锁轻量级锁就膨胀升级为重量级锁。一句话区分 重量级锁找OS内核帮忙线程挂起唤醒开销大。 轻量级锁JVM在用户态处理不依赖OS内核适合低竞争场景。补充偏向锁synchronized最底层优化偏向锁是轻量级锁的前置状态只有一个线程使用锁连CAS操作都尽量省略。三、挂起等待锁 VS 自旋锁拿不到锁时的两种等待方式1. 挂起等待锁阻塞锁抢锁失败线程被操作系统挂起休眠让出CPU。 锁释放之后操作系统再唤醒这个线程。 ✅优点不占用CPU线程休眠CPU可以跑别的任务 ❌缺点唤醒线程开销大用户态内核态切换。例子synchronized重量级锁、ReentrantLock默认非公平锁底层等待队列就是挂起等待2. 自旋锁抢锁失败不放弃CPU循环不断重试抢锁while循环不停检查锁是否释放。// 简易自旋锁伪代码 while (!尝试获取锁()) { // 原地循环反复尝试抢锁 }✅优点如果锁很快释放不需要线程切换速度快 ❌缺点如果锁长时间拿不到会一直循环空耗CPU例子CAS实现的自旋锁、Atomic类底层就是自旋生活例子 挂起等待锁排队办业务拿号之后坐到椅子上休息轮到你才叫你。不占用柜台资源但是叫号有延迟 自旋锁站在窗口前不停反复问“好了没有好了没有”一直循环询问不离开。短时间内很快长时间就很累汇总表格背诵版分类维度锁核心思想典型示例并发冲突预判悲观锁默认一定会冲突操作前上锁synchronized、ReentrantLock乐观锁默认不会冲突提交时校验冲突CAS、AtomicIntegersynchronized底层实现重量级锁依赖OS内核线程挂起高竞争下的synchronized轻量级锁用户态无内核调用低竞争交替使用锁的synchronized抢锁失败行为挂起等待锁抢不到锁线程休眠让出CPUsynchronized重量级锁自旋锁抢不到锁循环重试占用CPUCAS自旋锁⚠️高频易错点考试常坑悲观锁 ≠ 重量级锁。synchronized既是悲观锁又可以从轻量级锁膨胀到重量级锁。乐观锁没有阻塞依靠CAS自旋重试乐观锁不等于自旋锁。自旋锁只是等待策略可以是乐观锁也可以是悲观锁。轻量级锁不是乐观锁轻量级锁仍然属于悲观锁它假设会有竞争只是竞争不激烈。一句话速记悲观乐观预判会不会冲突轻重量级synchronized底层要不要找操作系统内核自旋/挂起抢不到锁是循环重试还是睡觉放弃CPU
返回列表