当前位置: 首页 > news >正文

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
http://www.jsqmd.com/news/794019/

相关文章:

  • 构建产品级AI智能体:五层架构与审美工程实战指南
  • 鸣潮自动化终极指南:5分钟解放双手,告别重复刷图
  • [具身智能-660]:具身智能系统 三层分级架构(基于 ROS2 分布式全域组网):上位机可视化层 + 中位机 AI 决策层 + 下位机实时感知执行层三级分层架构。
  • 从绕接到焊接:硬件连接技术的演进与工程思维启示
  • ARM TPIU调试接口原理与应用实践
  • 面向对象——面向对象基础
  • Docker镜像逆向分析:dfimage工具原理、实战与CI/CD应用
  • 从美光收购尔必达看DRAM产业格局:技术、市场与整合逻辑
  • 抖音下载器终极指南:3种场景下的高效内容获取方案
  • AI智能体规则引擎:从提示词约束到运行时控制的架构实践
  • openclaw官网入口中文版_一键1分钟免费使用小龙虾AI!
  • 手把手教你学Simulink——基于Simulink的储能PCS(功率转换系统)离网V/f控制仿真示例
  • 以太网技术演进:从局域网到万物互联的生态系统
  • SDN与IoT融合:构建云边端一体的智能网络神经系统
  • 【AI大模型春招面试题31】什么是“零样本学习(Zero-Shot)”“少样本学习(Few-Shot)”?大模型实现这类能力的核心原因?
  • 芯片验证覆盖率:从度量陷阱到有效策略的实战解析
  • 别再只盯着信号强度了!深入浅出解读LoRa天线S11、驻波比与回波损耗
  • 从硬件抽象到软件接口标准化:破解芯片设计中的驱动开发困局
  • EDA平台化架构:电子系统设计的未来趋势
  • 手把手教你学Simulink——【进阶版】单相并网逆变器比例谐振(PR)控制与谐波补偿仿真示例
  • java内存模型(JMM)
  • 嵌入式开发:从汇编到C语言的高效迁移与优化
  • AI+运维提效,ssl-cert-monitoring(SSL证书监控系统)2.0修复bug及新增功能说明
  • 软件设计原则之OCP开闭原则
  • 2026廊坊硅酸铝柔性包裹,防火专业厂家这样选
  • ARM虚拟化关键寄存器HIFAR与HMAIR详解
  • 终极指南:如何用Vue-Fabric-Editor解决轻量级图片编辑需求
  • 从EE Times标题竞赛看工程师文化:技术幽默如何驱动社区活力
  • 基于CMSIS和USB的嵌入式数据记录器开发指南
  • 高校普法系统|基于SSM高校普法系统(源码+数据库+文档)