卡码笔记
首页
计算机基础
C++
Java
面经
笔记广场 (opens new window)
代码随想录 (opens new window)
首页
计算机基础
C++
Java
面经
笔记广场 (opens new window)
代码随想录 (opens new window)
  • 基础与面向对象

  • 集合

  • 异常

  • 字符串

  • JVM

  • 并发与多线程

    • Java创建线程有几种方式?
    • 线程start和run的区别?
    • Java线程安全的实现?
    • synchronized和lock、reentrantlock的区别是什么?
    • 说一说你对synchronized的理解?
    • volatile关键字的作用有哪些?
    • volatile与synchronized的对比?
    • 你知道Java中有哪些锁吗?
      • 简要回答
      • 详细回答
      • 使用场景
      • 知识图解
      • 知识扩展
    • 为什么要有线程池,线程太多会怎样?
    • 说一说线程池的常用参数
    • BIO、NIO、AIO的区别
  • JDK

  • Spring

  • 设计模式

# Java锁

# 简要回答

Java中有很多关于锁的概念,可以分类成下面几个方面理解:

  1. 按照锁的获取机制(看待并发同步的角度):乐观锁/悲观锁
  2. 按照锁的竞争策略:公平锁/非公平锁
  3. 按照锁控制的资源范围:偏向锁/轻量级锁/重量级锁/分段锁
  4. 按照功能特性:可重入锁/读写锁/自旋锁/互斥锁
  5. 按照持有方式:独享锁/共享锁

# 详细回答

  1. 乐观锁:假设线程的并发访问不会发生冲突,操作时不加锁,在更新数据时采用尝试更新,如有冲突则重试。
    • 乐观锁在Java中即无锁编程
    • 例如原子类,通过CAS自旋实现原子操作的更新。
  2. 悲观锁:假设线程并发一定会有冲突,每次操作前必须先加锁,阻止其他线程干扰。
  3. 公平锁:线程按照申请锁的顺序获取锁,先等待先获得。
    • 优缺点:公平,但是效率低,存在线程唤醒开销。
  4. 非公平锁:线程获取锁时不按顺序,允许“插队”。
    • 优缺点:效率高,吞吐量大,但是可能导致优先级反转或饥饿现象。
  • 偏向锁、轻量级锁、重量级锁都是锁的状态,是针对synchronized的概念,通过对象监视器在对象头中的字段来表明的。
  1. 偏向锁:是JVM对synchronized的优化,如果只有一个线程访问同步资源,一旦线程获取锁,后续无需重复加锁。
  2. 轻量级锁:当偏向锁被多个线程竞争时升级为轻量级锁,其他线程会通过自旋尝试获取锁,不会阻塞。
  3. 重量级锁:轻量级锁自旋失败一定次数后升级为重量级锁,线程进入阻塞。重量级锁依赖操作系统的互斥量实现,重量级锁会使其他申请的线程进入阻塞,性能降低。
  4. 分段锁:将大对象拆分为多个小段,对每个段单独加锁,细化锁的粒度,减少锁竞争。
    • 分段锁是一种锁的设计,不是具体的锁。
    • 以ConcurrentHashMap中put操作为例,不会对整个hashmap加锁,会先通过hashcode计算放入的分段,对分段加锁。如果不是放在同一个分段中,可以实现并行插入。
  5. 可重入锁(递归锁):线程可以重复获取已持有的锁,避免自己锁死自己。
    • 实现方式:synchronized(隐式)ReentrantLock(显式)
// 由于可重入锁的特性,setB可以正常执行
synchronized void setA() throws Exception{
  Thread.sleep(1000);
  setB();
}
synchronized void setB() throws Exception{
  Thread.sleep(1000);
}
1
2
3
4
5
6
7
8
  1. 读写锁:区分“读操作”、“写操作”,允许多个读线程并发访问,读和写互斥,写和写互斥。
    • ReadWriteLock
  2. 自旋锁:线程获取锁失败时不立即阻塞,而是循环尝试获取锁,循环有次数限制。
    • 优缺点:减少线程上下文切换开销,但是循环会消耗CPU。
  3. 互斥锁:通过互斥机制保证同一时间只允许一个线程持有锁。
    • ReentrantLock
  • 互斥锁/读写锁是独享锁/共享锁的具体体现。
  1. 独享锁:同一时间只能有一个线程持有锁
    • ReentrantLock 是独享锁
    • ReadWriteLock 写锁是独享锁
  2. 共享锁:同一时间允许多个线程同时持有锁,线程间不互斥。
    • ReadWriteLock 读锁是共享锁,保证并发读高效,而读写、写读、写写的过程互斥。

# 使用场景

锁 使用场景
乐观锁 读操作频繁,冲突概率低的场景
悲观锁 写操作频繁,冲突概率高
偏向锁 单线程反复访问同步块
轻量级锁 短时间、低冲突并发场景
重量级锁 长耗时、高冲突并发场景
分段锁 对大对象的并发访问
读写锁 读多写少

Java中具体工具

工具 适用场景
synchronized 实现互斥锁。对共享资源的访问进行同步控制
ReentrantLock 实现可重入锁。可手动控制锁的获取和释放,支持公平锁,适合更高级别控制场景
ReadWriteLock 读写锁接口。适用于读多写少场景
StampedLock 乐观读写锁。并发性能更高,适用于读多写少场景。
AtomicInteger 基于CAS的原子操作类(无锁)。实现共享变量的原子更新

# 知识图解

  1. 锁的使用

image 2. CAS操作流程

image 3. 死锁的出现

# 知识扩展

  1. 扩展:
  • CAS操作(Compare and Swap)是一种无锁的原子操作机制,广泛应用于多线程编程中,实现高效的线程安全。
    • CAS有三个操作数:内存位置(V),预期原值(A),新值(B)。
    • 具体操作:
      1. 从内存位置V读取当前值。
      2. 比较当前值和预期原值A是否相等。
      3. 如果相等就将内存位置V的值更新为B。
      4. 如果不相等,则说明该内存位置的值已经被修改过了,则不进行更新操作,选择重试或者执行其他逻辑。
  • 死锁
    • 两个线程分别对两个共享资源使用了两个互斥锁,造成两个线程都在等待对方释放锁。
    • 需要同时满足四个条件:
      1. 互斥条件
      2. 持有并等待条件
      3. 不可剥夺条件
      4. 环路等待条件
  1. 面试官可能追问:
  • Q1:如何避免死锁的产生?
    • 破坏死锁出现的四个条件之一就可以避免死锁产生。
    1. 破坏环路等待条件:强制所有线程按固定顺序申请资源。是最常用和有效的避免死锁产生措施。
    2. 破坏持有并等待条件:线程在执行任务前一次性申请所有需要的资源,如果无法申请其中某资源,则释放已申请的所有资源并等待。
    3. 破坏不可剥夺条件:允许资源被强制回收。
    4. 破坏互斥条件:将资源设计成共享资源,局限性高,不常使用。
Last Updated: 3/10/2026, 6:08:48 PM

← volatile与synchronized的对比? 为什么要有线程池,线程太多会怎样? →

评论

验证登录状态...

侧边栏
夜间
卡码简历
代码随想录
卡码投递表🔥
2026群
添加客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记
鼓励/支持/赞赏Carl
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪