news 2026/5/11 5:53:36

Java多线程:从入门到进阶

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java多线程:从入门到进阶

Java多线程:从入门到进阶

1. 引入:为什么需要多线程?

1.1 单线程的瓶颈

假设你要下载三个文件,单线程的做法是:一个个下载,总时间 = 文件1 + 文件2 + 文件3。

downloadFile1();// 等待完成downloadFile2();// 再等downloadFile3();// 最后// 总耗时很长

如果能让三个下载同时进行,总时间 ≈ 最慢的那个文件 —— 这就是多线程的价值。

1.2 多线程能做什么

  • 提升响应速度:UI不会卡死,后台任务异步处理
  • 充分利用CPU:多核CPU真正并行
  • 提高资源利用率:一个线程等待IO时,其他线程可继续工作

1.3 需要注意

多线程不是完美的,它带来:

  • 线程安全问题:多个线程同时修改共享数据
  • 性能开销:创建、切换线程有成本
  • 死锁风险:互相等待对方释放资源

2. 线程的创建方式

Java中有三种常见方式创建线程。

2.1 继承Thread类

classMyThreadextendsThread{@Overridepublicvoidrun(){System.out.println(Thread.currentThread().getName()+"执行");}}// 使用MyThreadt1=newMyThread();t1.start();// 启动线程(不是调用run)

特点

  • 简单直接
  • 单继承局限(不能再继承其他类)

2.2 实现Runnable接口

classMyRunnableimplementsRunnable{@Overridepublicvoidrun(){System.out.println(Thread.currentThread().getName()+"执行");}}// 使用Threadt1=newThread(newMyRunnable());t1.start();

Lambda简化(推荐):

Threadt1=newThread(()->{System.out.println(Thread.currentThread().getName()+"执行");});t1.start();

特点

  • 避免单继承局限
  • 更适合资源共享(多个线程执行同一个Runnable实例)

2.3 实现Callable接口 + FutureTask

有返回值的线程:

classMyCallableimplementsCallable<Integer>{@OverridepublicIntegercall()throwsException{Thread.sleep(1000);return100;}}// 使用FutureTask<Integer>task=newFutureTask<>(newMyCallable());Threadt1=newThread(task);t1.start();Integerresult=task.get();// 获取返回值(会阻塞直到完成)

特点

  • 可以有返回值
  • 可以抛异常

2.4 三种方式对比

方式是否有返回值可否抛异常是否单继承适用场景
继承Thread简单任务
Runnable推荐,资源共享
Callable需要返回结果

3. 线程的常用方法

方法作用
start()启动线程,JVM调用run()
run()线程执行体,不要直接调用
sleep(long millis)线程睡眠(让出CPU,不释放锁)
join()等待该线程终止
yield()礼让,让出CPU,重新参与竞争
setPriority(int)设置优先级(1~10,默认5)
interrupt()中断线程(设置中断标志)
currentThread()获取当前执行的线程
Threadt1=newThread(()->{for(inti=0;i<5;i++){System.out.println("t1: "+i);Thread.sleep(500);}});Threadt2=newThread(()->{try{t1.join();// t2等待t1执行完}catch(InterruptedExceptione){}System.out.println("t2执行");});t1.start();t2.start();

4. 线程的生命周期(状态)

线程有6种状态:

NEW → RUNNABLE → TERMINATED ↙ ↘ BLOCKED WAITING / TIMED_WAITING
状态说明
NEW创建但未启动(还没调用start)
RUNNABLE可运行状态(正在JVM中执行,可能等待CPU)
BLOCKED阻塞,等待获取锁(synchronized)
WAITING无限等待(wait()join()无超时)
TIMED_WAITING限时等待(sleep()wait(timeout)
TERMINATED结束(run执行完)
Threadt=newThread(()->{});System.out.println(t.getState());// NEWt.start();System.out.println(t.getState());// RUNNABLE(可能瞬间变化)

5. 线程安全问题

5.1 问题演示

多个线程同时修改共享变量:

classCounter{privateintcount=0;publicvoidincrement(){count++;}publicintgetCount(){returncount;}}// 两个线程各加1000次Countercounter=newCounter();Threadt1=newThread(()->{for(inti=0;i<1000;i++)counter.increment();});Threadt2=newThread(()->{for(inti=0;i<1000;i++)counter.increment();});t1.start();t2.start();t1.join();t2.join();System.out.println(counter.getCount());// 期望2000,实际可能小于2000

原因count++不是原子操作(读-改-写),线程交叉执行导致数据丢失。

5.2 解决方案:synchronized

同步代码块
publicvoidincrement(){synchronized(this){// 锁当前对象count++;}}
同步方法
publicsynchronizedvoidincrement(){// 等效于synchronized(this)count++;}
静态同步方法(类锁)
publicstaticsynchronizedvoidstaticMethod(){// 锁Class对象// ...}

5.3 锁的选择

方式锁对象影响范围
非静态同步方法this同一个对象的多个方法互斥
静态同步方法类名.class所有对象的该方法互斥
同步代码块自定义更灵活,可减小锁粒度

6. 线程通信:wait/notify

生产者-消费者模式经典示例:

classProduct{privateintcount=0;privatefinalintMAX=10;publicsynchronizedvoidproduce()throwsInterruptedException{while(count>=MAX){wait();// 满了,等待消费}count++;System.out.println("生产,库存:"+count);notifyAll();// 唤醒等待的消费者}publicsynchronizedvoidconsume()throwsInterruptedException{while(count<=0){wait();// 空了,等待生产}count--;System.out.println("消费,库存:"+count);notifyAll();}}

6.1 wait/notify的规则

规则说明
必须在synchronized块内否则抛IllegalMonitorStateException
wait释放锁让出锁,进入WAITING
notify不释放锁唤醒另一个线程,但需要当前线程退出同步块后才实际竞争
用while循环判断条件防止虚假唤醒

6.2 wait和sleep的区别

维度waitsleep
所属Object的方法Thread的静态方法
释放锁
需要synchronized
唤醒条件notify/notifyAll时间到或interrupt

7. 线程安全的集合

集合线程安全方式适用场景
Vector/Hashtable内部方法synchronized遗留类,不推荐
Collections.synchronizedXxx包装成同步简单的同步需求
CopyOnWriteArrayList写时复制读多写少
ConcurrentHashMap分段锁/CAS高并发Map
BlockingQueue阻塞队列生产者-消费者
// 推荐:ConcurrentHashMapMap<String,String>map=newConcurrentHashMap<>();// 线程安全的ListList<String>list=newCopyOnWriteArrayList<>();

8. 并发问题的底层根源(了解)

根源说明
可见性一个线程修改共享变量,其他线程看不到(用volatile解决)
原子性操作不是一步完成(用synchronizedLock
有序性指令重排序(volatile禁止重排序)

volatile关键字

  • 保证可见性:修改立刻写回主存
  • 禁止指令重排序
  • 不保证原子性(如count++仍不安全)
volatilebooleanflag=true;// 适合用作开关

9. 线程池

9.1 为什么需要线程池?

每次创建和销毁线程都有开销:

  • new Thread()创建线程 → 内存分配、系统调用
  • 线程用完销毁 → 垃圾回收

如果有大量短任务,反复创建销毁线程会严重影响性能。

线程池的优势

  • 复用线程,减少创建销毁开销
  • 控制线程数量,防止资源耗尽
  • 统一管理任务队列

9.2 线程池的核心:ThreadPoolExecutor

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

执行流程

提交任务 → 核心线程未满 → 创建核心线程执行 → 核心线程已满 → 任务放入队列 → 队列已满 → 创建非核心线程执行 → 达到最大线程 → 执行拒绝策略

9.3 五种常见的线程池

方法说明特点
newFixedThreadPool(n)固定线程数核心=最大=n,无超时
newCachedThreadPool()缓存线程池核心=0,最大=无限,空闲60秒回收
newSingleThreadExecutor()单线程池核心=最大=1,保证顺序执行
newScheduledThreadPool(n)定时任务池支持延迟和执行周期任务
newWorkStealingPool()工作窃取池基于ForkJoinPool(Java 8+)
// 固定大小线程池ExecutorServicepool=Executors.newFixedThreadPool(5);// 提交任务pool.execute(()->{System.out.println("无返回值任务");});Future<String>future=pool.submit(()->{return"有返回值任务";});// 关闭线程池pool.shutdown();// 不再接受新任务,等待已有任务完成pool.shutdownNow();// 尝试停止所有执行中任务

9.4 任务队列的选择

队列类型特点适用场景
ArrayBlockingQueue有界,数组结构任务数量可控
LinkedBlockingQueue有界/无界,链表结构默认无界,小心OOM
SynchronousQueue不存储任务,需立即处理cached线程池使用
PriorityBlockingQueue有优先级任务需排序

9.5 使用线程池的注意事项

易错点说明
不推荐Executors.newFixedThreadPool阻塞队列无界,可能OOM
不推荐Executors.newCachedThreadPool最大线程无限,可能创建过多线程
忘记关闭线程池程序可能不会退出
shutdown和shutdownNow混淆shutdown不再收任务但执行已有;shutdownNow尝试中断
异常被吞掉submit返回的Future可以捕获异常

阿里巴巴规范:不要用Executors创建线程池,要自定义ThreadPoolExecutor

9.6 完整示例

ThreadPoolExecutorpool=newThreadPoolExecutor(2,5,60L,TimeUnit.SECONDS,newArrayBlockingQueue<>(100),r->{Threadt=newThread(r);t.setName("MyPool-"+System.currentTimeMillis());returnt;},(r,executor)->{System.out.println("任务被拒绝:"+r.toString());});// 提交任务for(inti=0;i<200;i++){finalinttaskId=i;pool.execute(()->{System.out.println(Thread.currentThread().getName()+"执行任务"+taskId);});}pool.shutdown();try{if(!pool.awaitTermination(60,TimeUnit.SECONDS)){pool.shutdownNow();}}catch(InterruptedExceptione){pool.shutdownNow();}

10. 死锁

10.1 死锁示例

两个线程互相持有对方需要的锁:

ObjectlockA=newObject();ObjectlockB=newObject();Threadt1=newThread(()->{synchronized(lockA){Thread.sleep(100);synchronized(lockB){}}});Threadt2=newThread(()->{synchronized(lockB){Thread.sleep(100);synchronized(lockA){}}});

10.2 避免方法

  • 按固定顺序获取锁
  • 使用tryLock超时放弃
  • 减少锁的嵌套

11. 易错点总结

易错点错误原因正确做法
直接调用run()以为会启动线程调用start()
静态方法synchronized混用锁对象不同明确锁是类还是实例
wait/notify不用while判断虚假唤醒用while循环
线程安全问题只想到synchronized简单粗暴考虑volatile、原子类、安全集合
死锁排查困难互相等待用jstack分析
共享变量不加同步以为不会并发所有共享变量都要同步
Executors创建线程池无界队列OOM自定义ThreadPoolExecutor
忘记关闭线程池程序不退出finally中shutdown

12. 总结对比表

概念核心要点
创建线程三种方式:Thread、Runnable、Callable
生命周期NEW → RUNNABLE → TERMINATED(中间有阻塞/等待)
线程安全synchronized、Lock、原子类、安全集合
线程通信wait/notify、BlockingQueue
并发关键字volatile(可见性、禁止重排)、synchronized(原子性)
线程池ThreadPoolExecutor核心参数、任务队列
死锁循环等待,按顺序加锁或tryLock
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/11 5:48:10

基于Vagrant的Claude本地部署:自动化AI开发环境搭建指南

1. 项目概述&#xff1a;一个让Claude在本地“安家”的Vagrant包装器 如果你和我一样&#xff0c;是个喜欢在本地环境折腾各种AI工具的开发人员&#xff0c;那你肯定对Claude这个强大的语言模型不陌生。但官方提供的使用方式往往受限于网络环境、API调用成本或者隐私顾虑&…

作者头像 李华
网站建设 2026/5/11 5:37:31

Arm SME架构下的矩阵乘法优化实践

1. 矩阵乘法优化基础与SME架构概述矩阵乘法作为高性能计算中的核心操作&#xff0c;其优化水平直接影响机器学习、科学计算等领域的性能表现。传统优化方法通常依赖于特定硬件平台的向量指令集&#xff0c;而Arm SME&#xff08;Scalable Matrix Extension&#xff09;架构的引…

作者头像 李华
网站建设 2026/5/11 5:35:32

启航 —— 二本NPC程序学习之路

我是来自福建省龙岩市一个小县城的二本学生&#xff0c;学习成绩没有很出色&#xff0c;虽然在我们县城那里还挺优秀的&#xff0c;但是我知道这还是远远不够的。于是我选择了计算机专业这条方向&#xff0c;希望之后能通过自己的努力&#xff0c;将来能找到一份自己满意的工作…

作者头像 李华
网站建设 2026/5/11 5:32:33

免费开源网盘直链下载工具:八大主流网盘完整使用指南

免费开源网盘直链下载工具&#xff1a;八大主流网盘完整使用指南 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 &#xff0c;支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云…

作者头像 李华
网站建设 2026/5/11 5:29:33

txtskills:将llms.txt文档一键转换为AI助手可调用技能

1. 项目概述与核心价值最近在折腾AI编程助手&#xff0c;发现一个挺有意思的痛点&#xff1a;很多优秀的开源项目或者技术文档&#xff0c;都开始用一种叫llms.txt的格式来写说明。这格式对AI很友好&#xff0c;但问题是&#xff0c;我的Claude Code、Cursor或者GitHub Copilot…

作者头像 李华
网站建设 2026/5/11 5:29:32

为什么你的评分卡在B银行是神器,在C平台就是废纸?

一方水土养一方人,同样也养着一方评分卡。“这套评分卡在B银行跑了好几年,KS值稳定在30%以上,简直是风控神器!可到了我们C平台,怎么就成了没人看的废纸?”最近又听到一位从银行跳槽到互联网平台的朋友抱怨。这场景太熟悉了——行业通用的最佳实践,拿过来就水土不服。这背…

作者头像 李华