十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

并发安全与锁机制

并发安全与锁机制 并发安全与锁机制在后端开发、服务端编程、高并发系统开发中并发安全是绕不开的核心话题。而锁是解决并发安全问题最基础、最核心的手段之一。很多开发者日常开发中都会用到锁但大多只停留在“加锁防止数据错乱”的表层认知不清楚锁的底层原理、不同锁的适用场景也时常遇到死锁、性能卡顿、锁失效等问题。一、什么是并发安全不安全的根源是什么1. 并发安全定义多线程、多进程同时操作共享资源共享变量、数据库数据、文件、缓存等时程序最终执行结果和串行执行结果一致且不会出现数据脏读、覆盖、错乱等问题即为并发安全。反之就是并发不安全。2. 并发不安全的三大核心原因以最常见的多线程编程为例并发安全问题本质由三个计算机特性导致1原子性破坏原子性指一个操作要么全部执行成功要么完全不执行不可被中断。我们日常的i操作看似是一行代码底层实际分为三步读取变量值、计算新值、写入新值。在多线程场景下这三步可能被多个线程穿插执行导致数据覆盖、计数不准这就是原子性缺失导致的安全问题。2可见性问题现代CPU为了提升性能会存在缓存机制。每个线程会将共享变量从主内存拷贝到自己的工作内存CPU缓存。当一个线程修改了变量值并写入工作内存其他线程无法立即感知到这个变化会一直读取旧值导致数据可见性失效。3有序性重排编译器、CPU为了优化执行效率会在不影响单线程执行结果的前提下对代码指令进行重排序。单线程下指令重排无任何影响但多线程场景下重排会导致代码执行顺序错乱引发未知的业务异常。二、锁的核心作用解决并发三大问题锁的本质就是通过资源竞争、互斥访问的机制强制让共享资源在同一时刻只被一个线程操作从而兜底解决原子性、可见性、有序性三大问题最终保证并发安全。简单来说锁是并发场景下共享资源的“访问通行证”只有拿到锁的线程才能操作资源未拿到锁的线程只能等待。三、主流锁分类与实战场景锁的种类繁多我们不用死记硬背只需按照悲观/乐观、公平/非公平、可重入/不可重入三大维度区分结合场景使用即可。1. 悲观锁 vs 乐观锁核心分类1悲观锁核心思想默认并发一定会出现冲突因此每次操作共享资源前都会先加锁独占资源阻止其他线程访问。特点加锁开销大、阻塞等待、并发性能低、安全性极高。常见实现synchronized、ReentrantLock、数据库行锁/表锁。适用场景写多读少、冲突频繁的场景比如订单扣款、库存扣减、数据修改。2乐观锁核心思想默认并发冲突极少发生因此全程不加锁直接操作资源只在提交结果时校验是否发生冲突冲突则重试或放弃。特点无锁开销、不阻塞、并发性能高、冲突时需要重试。常见实现CAS自旋、版本号机制数据库version、时间戳校验。适用场景读多写少、冲突概率低的场景比如商品详情查询、用户信息读取。2. 可重入锁 vs 不可重入锁1可重入锁同一个线程拿到锁后可以再次重复获取该锁不会发生自我阻塞避免死锁问题。日常开发中synchronized、ReentrantLock都是可重入锁也是我们最常用的锁。举例A方法加锁后调用同样加锁的B方法可重入锁可以正常执行无需释放锁再竞争。2不可重入锁同一个线程无法重复获取已持有的锁再次获取会直接阻塞极易引发死锁现代开发中基本不再手动使用。3. 公平锁 vs 非公平锁1公平锁线程按照请求锁的先后顺序排队获取锁先到先得无线程插队。特点线程切换开销大、吞吐量低、线程执行公平无饥饿问题。2非公平锁线程获取锁不遵循排队顺序新线程可以直接抢占锁大概率比排队的老线程先拿到锁。特点减少线程切换、吞吐量高、性能更好是默认锁模式ReentrantLock、synchronized默认非公平。四、开发高频锁synchronized 与 ReentrantLock 对比Java 开发中最常用的两把锁就是 synchronized 和 ReentrantLock很多人不知道该如何选择这里直接给出对比结论和使用规范。1. synchronized内置锁特性JVM原生锁、自动加锁释放、可重入、非公平、不可中断优点使用简单、无需手动释放锁、底层持续优化偏向锁/轻量级锁/重量级锁缺点灵活性差、无法超时等待、无法精准唤醒线程适用简单的代码块同步、低并发场景2. ReentrantLock显式锁特性JDK代码实现、手动加锁释放、可重入、可公平/非公平、可中断、可超时优点灵活性极强、支持锁超时、条件精准唤醒、高并发性能稳定缺点需要手动解锁容易遗漏unlock()引发死锁适用高并发、复杂同步逻辑、需要超时控制、精准唤醒的场景3. 最终选择原则简单同步逻辑、低并发 → 优先synchronized高并发、需要超时、精准控制、复杂业务 → 优先ReentrantLock五、并发锁高频坑与解决方案1. 锁失效问题常见场景锁对象被修改、锁的是成员变量而非全局对象、字符串锁常量池复用。原因锁的本质是锁定对象的监视器对象变了锁就彻底失效。解决方案使用final 修饰锁对象保证锁对象唯一且不可修改。2. 死锁问题死锁四大必要条件互斥、请求保持、不可剥夺、循环等待。最常见场景多个线程互相持有对方需要的锁且同时等待对方释放锁。解决方案统一锁的获取顺序避免循环等待使用 ReentrantLock 的 tryLock 超时机制超时自动释放锁减少嵌套锁尽量只持有一把锁。3. 锁粒度太大导致性能雪崩问题为了简单直接锁住整个方法、整个业务逻辑大量线程阻塞排队并发完全退化串行。解决方案减小锁粒度只锁住共享资源操作的核心代码块无关逻辑尽量移出锁范围。4. 读写并发低效问题问题普通排他锁会让读写、读读之间互相阻塞读多场景下性能极差。解决方案使用ReentrantReadWriteLock 读写锁读读共享、读写互斥、写写互斥大幅提升读场景并发性能。六、总结并发不安全的本质是原子性、可见性、有序性三大特性缺失锁的核心价值是通过互斥访问兜底保证共享资源的并发安全悲观锁适合写多场景乐观锁适合读多场景按需选择简单场景用 synchronized高并发复杂场景用 ReentrantLock并发编程的核心不是滥用锁而是最小化锁范围、避免锁竞争、杜绝锁坑。掌握并发安全与锁机制是从初级开发者进阶为高级后端工程师的必经之路。后续遇到并发数据异常、接口卡顿、死锁问题时都可以从本文的原理和解决方案中快速定位问题。
返回列表