news 2026/6/25 19:24:01

Java多线程基础(2)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java多线程基础(2)

Java 多线程进阶 —— Lock 锁与线程池

作者:没有四次元口袋的蓝胖
日期:2026-06-25
标签:Java, 多线程


一、Lock 锁的基本使用

1.1 为什么需要 Lock?

synchronized 是 JVM 内置的锁,用法简单但不够灵活:

  • 无法中断等待
  • 无法设置超时
  • 无法尝试非阻塞获取锁
  • 锁的获取和释放绑定在一起

java.util.concurrent.locks.Lock接口提供了更灵活的锁操作。

1.2 Lock 接口核心方法

publicinterfaceLock{voidlock();// 阻塞获取锁voidlockInterruptibly();// 可响应中断的获取锁booleantryLock();// 尝试非阻塞获取锁,立即返回booleantryLock(longtime,TimeUnitunit);// 超时尝试voidunlock();// 释放锁ConditionnewCondition();// 获取条件变量(类似 wait/notify)}

1.3 ReentrantLock(最常用)

可重入锁,功能最全面的 Lock 实现:

ReentrantLocklock=newReentrantLock();// 默认非公平锁// ReentrantLock lock = new ReentrantLock(true); // 公平锁lock.lock();try{// 临界区代码System.out.println("业务逻辑");}finally{lock.unlock();// ⚠️ 必须在 finally 中释放!}

⚠️ 关键:unlock() 必须放在 finally 块中!否则异常时锁不会释放,导致死锁。

1.4 公平锁 vs 非公平锁

特性公平锁非公平锁
获取顺序按等待顺序(FIFO)可以插队
吞吐量较低(频繁切换线程)较高
饥饿问题不会可能
默认是(推荐)
// 非公平锁(默认,性能好)ReentrantLockfairLock=newReentrantLock(false);// 公平锁ReentrantLockunfairLock=newReentrantLock(true);

1.5 tryLock 的用法

ReentrantLocklock=newReentrantLock();// 1. 非阻塞尝试if(lock.tryLock()){try{// 获取成功}finally{lock.unlock();}}else{// 获取失败,做其他事System.out.println("没抢到锁,干点别的");}// 2. 超时尝试if(lock.tryLock(3,TimeUnit.SECONDS)){try{// 3秒内获取成功}finally{lock.unlock();}}else{System.out.println("等了3秒还是没抢到");}

1.6 Condition 条件变量

类似 Object 的 wait/notify,但更灵活(一个锁可以有多个条件):

ReentrantLocklock=newReentrantLock();ConditionnotFull=lock.newCondition();// "没满"条件ConditionnotEmpty=lock.newCondition();// "没空"条件// 生产者lock.lock();try{while(isFull()){notFull.await();// 满了就等}produce();notEmpty.signal();// 通知消费者}finally{lock.unlock();}// 消费者lock.lock();try{while(isEmpty()){notEmpty.await();// 空了就等}consume();notFull.signal();// 通知生产者}finally{lock.unlock();}

对比 wait/notify:

特性synchronized + wait/notifyLock + Condition
条件数量1个可创建多个
精确唤醒不能(只能 notifyAll)signal() 精确唤醒
等待是否可中断不可await() 可响应中断
超时等待不支持await(timeout) 支持

1.7 ReentrantReadWriteLock(读写锁)

ReentrantReadWriteLockrwLock=newReentrantReadWriteLock();ReentrantReadWriteLock.ReadLockreadLock=rwLock.readLock();ReentrantReadWriteLock.WriteLockwriteLock=rwLock.writeLock();// 读操作(多个线程可以同时获取读锁)readLock.lock();try{// 读数据}finally{readLock.unlock();}// 写操作(独占)writeLock.lock();try{// 写数据}finally{writeLock.unlock();}

读锁特点:

  • 读读不互斥(多个线程可以同时读)
  • 读写互斥
  • 写写互斥
  • 适合读多写少的场景

1.8 Lock 与 synchronized 对比

特性synchronizedLock
实现层面JVM 关键字JDK API
锁释放自动释放必须手动 unlock()
可中断不可中断lockInterruptibly() 可中断
超时获取不支持tryLock(timeout) 支持
公平性非公平可选公平/非公平
条件变量一个(wait/notify)多个 Condition
性能JDK 6+ 优化后差不多差不多
代码复杂度稍高(要写 try-finally)

📌 常见坑点与面试题

Q:Lock 的 unlock() 为什么必须放在 finally 中?
如果临界区抛异常,unlock() 不会执行,锁永远不释放 → 死锁。finally 保证无论是否异常都能释放。

Q:ReentrantLock 的"可重入"是什么意思?
同一线程可以重复获取同一把锁而不会死锁。内部维护计数器,每次 lock() 加1,unlock() 减1,减到0才真正释放。

Q:公平锁性能为什么比非公平锁差?
公平锁要维护等待队列,每次释放锁都要唤醒队列头部的线程,涉及线程上下文切换。非公平锁允许新来的线程直接 CAS 抢锁,减少了切换开销。


二、线程池

2.1 为什么用线程池?

  • 降低资源消耗:复用已创建的线程,避免频繁创建和销毁
  • 提高响应速度:任务到达时,无需等线程创建就能执行
  • 便于管理:统一管理线程资源,可以控制并发数、排队、拒绝策略

2.2 四种常见线程池(Executors 工厂方法)

// 1. 固定大小线程池ExecutorServicefixed=Executors.newFixedThreadPool(4);// 核心线程数 = 最大线程数 = 4,队列无界(LinkedBlockingQueue)// 2. 缓存线程池ExecutorServicecached=Executors.newCachedThreadPool();// 核心线程数 = 0,最大线程数 = Integer.MAX_VALUE// 60秒空闲回收,适合大量短任务// 3. 单线程线程池ExecutorServicesingle=Executors.newSingleThreadExecutor();// 保证所有任务按顺序执行(FIFO)// 4. 定时线程池ScheduledExecutorServicescheduled=Executors.newScheduledThreadPool(2);// 支持定时和周期性任务

⚠️ 阿里规范禁止使用 Executors 创建线程池!原因:

  • newFixedThreadPool/newSingleThreadExecutor:队列无界(LinkedBlockingQueue 容量 Integer.MAX_VALUE),可能堆积大量任务导致 OOM
  • newCachedThreadPool:最大线程数 Integer.MAX_VALUE,可能创建大量线程导致 OOM

2.3 ThreadPoolExecutor(自定义线程池 ✅ 推荐)

这才是线程池的真正核心类,Executors 的工厂方法底层也是调它:

publicThreadPoolExecutor(intcorePoolSize,// 核心线程数intmaximumPoolSize,// 最大线程数longkeepAliveTime,// 非核心线程空闲存活时间TimeUnitunit,// 时间单位BlockingQueue<Runnable>workQueue,// 任务队列ThreadFactorythreadFactory,// 线程工厂(可选)RejectedExecutionHandlerhandler// 拒绝策略(可选))

2.4 七大参数详解

参数作用典型值
corePoolSize核心线程数,即使空闲也不回收CPU核心数 或 CPU核心数+1
maximumPoolSize最大线程数CPU密集:N+1;IO密集:2N
keepAliveTime非核心线程空闲存活时间60s
unit时间单位TimeUnit.SECONDS
workQueue任务队列ArrayBlockingQueue / LinkedBlockingQueue
threadFactory创建线程的工厂可自定义线程名方便排查
handler拒绝策略4种内置策略

2.5 线程池工作流程 ⭐⭐⭐

任务提交 ↓ 核心线程数满了吗? ├── 没满 → 创建核心线程执行任务 └── 满了 → 任务队列满了吗? ├── 没满 → 任务放入队列 └── 满了 → 线程数达到最大值了吗? ├── 没达到 → 创建非核心线程执行任务 └── 达到了 → 执行拒绝策略

一句话总结:核心线程 → 队列 → 最大线程 → 拒绝策略

2.6 四种拒绝策略

策略行为
AbortPolicy(默认)抛出 RejectedExecutionException
CallerRunsPolicy由提交任务的线程自己执行(调用者运行)
DiscardPolicy直接丢弃,不抛异常
DiscardOldestPolicy丢弃队列中最老的任务,重新提交

2.7 自定义线程池示例

ThreadPoolExecutorpool=newThreadPoolExecutor(4,// 核心线程数8,// 最大线程数60,TimeUnit.SECONDS,// 非核心线程存活60秒newArrayBlockingQueue<>(100),// 有界队列,容量100newThreadFactory(){privatefinalAtomicIntegercount=newAtomicInteger(1);@OverridepublicThreadnewThread(Runnabler){Threadt=newThread(r);t.setName("my-pool-thread-"+count.getAndIncrement());returnt;}},newThreadPoolExecutor.CallerRunsPolicy()// 拒绝策略:调用者执行);// 提交任务pool.execute(()->{System.out.println(Thread.currentThread().getName()+" 执行任务");});// 关闭线程池pool.shutdown();// 不再接收新任务,等已提交的任务执行完// pool.shutdownNow(); // 尝试中断所有任务,返回未执行的任务列表

2.8 线程池常用方法

方法作用
execute(Runnable)提交任务(无返回值)
submit(Runnable/Callable)提交任务,返回 Future
shutdown()优雅关闭,不接收新任务,等现有任务完成
shutdownNow()强制关闭,尝试中断正在执行的任务
isShutdown()是否已关闭
isTerminated()所有任务是否都完成
getPoolSize()当前线程数
getActiveCount()活跃线程数
getQueue().size()队列中等待的任务数

submit + Future 获取返回值:

Future<String>future=pool.submit(()->{Thread.sleep(1000);return"任务完成";});Stringresult=future.get();// 阻塞等待结果// future.get(2, TimeUnit.SECONDS); // 超时等待

2.9 线程池大小怎么设?

任务类型公式原因
CPU 密集型N + 1(N = CPU核心数)减少线程切换开销
IO 密集型2N 或更多IO等待时CPU空闲,多利用
混合型拆分为CPU任务和IO任务,分别建池各自优化
intcpuCores=Runtime.getRuntime().availableProcessors();// CPU密集型intcpuPoolSize=cpuCores+1;// IO密集型(假设IO等待时间占比80%)intioPoolSize=cpuCores*2;

📌 常见坑点与面试题

Q:为什么禁止用 Executors 创建线程池?
newFixedThreadPool 和 newSingleThreadExecutor 用 LinkedBlockingQueue 无界队列,任务堆积可能 OOM;newCachedThreadPool 最大线程数 Integer.MAX_VALUE,可能创建大量线程 OOM。推荐手动 new ThreadPoolExecutor,参数明确可控。

Q:核心线程会不会被回收?
默认不会。设置 allowCoreThreadTimeOut(true) 后,核心线程空闲超时也会被回收。

Q:线程池的线程数可以动态调整吗?
可以。setCorePoolSize() 和 setMaximumPoolSize() 可以在运行时修改。


三、思维导图速览

Lock锁与线程池 ├── Lock 锁 │ ├── Lock 接口(lock/tryLock/unlock/newCondition) │ ├── ReentrantLock │ │ ├── 可重入(计数器实现) │ │ ├── 公平锁 vs 非公平锁 │ │ └── tryLock(非阻塞/超时尝试) │ ├── Condition(精确唤醒,替代 wait/notify) │ ├── ReentrantReadWriteLock(读读共享、写写互斥) │ └── Lock vs synchronized 对比 └── 线程池 ├── 为什么用(复用、响应快、易管理) ├── 四种工厂方法(阿里规范禁止用) ├── ThreadPoolExecutor 七大参数 ├── 工作流程:核心线程→队列→最大线程→拒绝策略 ├── 四种拒绝策略 ├── submit + Future 获取返回值 └── 线程池大小:CPU密集 N+1,IO密集 2N

四、写在最后

  1. Lock 部分:重点掌握 ReentrantLock 的 try-finally 写法,面试常问 Lock 和 synchronized 的区别,要能说出公平锁/非公平锁、Condition 的优势
  2. 线程池部分:面试高频中的高频!七大参数和工作流程必须烂熟,"为什么不用 Executors"几乎是必考题
  3. 建议动手写:自定义一个线程池,提交不同数量的任务,观察线程创建和拒绝策略的触发过程
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/25 19:10:53

3步快速上手:无需训练的AI换脸工具终极指南

3步快速上手&#xff1a;无需训练的AI换脸工具终极指南 【免费下载链接】roop-unleashed Evolved Fork of roop with Web Server and lots of additions 项目地址: https://gitcode.com/gh_mirrors/ro/roop-unleashed 你是否曾经想过&#xff0c;只需几分钟就能制作出电…

作者头像 李华
网站建设 2026/6/25 19:03:48

NxNandManager终极指南:如何轻松管理你的任天堂Switch NAND存储

NxNandManager终极指南&#xff1a;如何轻松管理你的任天堂Switch NAND存储 【免费下载链接】NxNandManager Nintendo Switch NAND management tool : explore, backup, restore, mount, resize, create emunand, etc. (Windows) 项目地址: https://gitcode.com/gh_mirrors/n…

作者头像 李华
网站建设 2026/6/25 18:58:54

AutoCAD 2027

AutoCAD 2027是Autodesk推出的旗舰级专业CAD设计软件&#xff0c;适配Windows 10/11及macOS系统&#xff0c;兼顾2D精确绘图与3D复杂建模&#xff0c;聚焦AI智能赋能与云端协同升级&#xff0c;是建筑、机械、土木、电子等多行业设计师的核心创作工具。该版本核心升级AI辅助功能…

作者头像 李华
网站建设 2026/6/25 18:57:48

SGLang:每天处理万亿 token 的 LLM 推理引擎

文章目录SGLang&#xff1a;每天处理万亿 token 的 LLM 推理引擎速度是核心优势硬件和模型覆盖面不止是推理SGLang&#xff1a;每天处理万亿 token 的 LLM 推理引擎 大语言模型的推理速度&#xff0c;一直是工程团队头疼的问题。模型越来越强&#xff0c;部署成本居高不下。SG…

作者头像 李华
网站建设 2026/6/25 18:56:44

Triton+FastAPI模型服务化:高可用ML在线推理实战

1. 项目概述&#xff1a;当模型走出Jupyter&#xff0c;真正开始呼吸真实世界的空气 “From Notebook to Production: Running ML in the Real World (Part 4)”——这个标题本身就像一句暗号&#xff0c;专为那些在Jupyter里调通了模型、画出了漂亮ROC曲线、却在部署时被生产环…

作者头像 李华