Java 多线程超详细整理,从入门到精通
多线程是 Java 并发编程核心内容,程序异步处理、高并发场景都依赖多线程实现。本文围绕进程线程概念、三种创建线程方式、线程常用 API、线程安全与同步锁、线程池、自定义线程池七大模块完整讲解,搭配卖票实战案例、完整可运行代码、表格汇总、开发规范与面试考点,适合零基础自学、期末复习、面试查漏补缺。
一、进程和线程
1.1 进程概述
进程是程序在数据集合上的一次运行活动,操作系统资源分配最小单位,简单理解:电脑中运行的软件程序。进程三大特性:
- 独立性:每个进程拥有独立内存空间,无法直接访问其他进程数据;
- 动态性:进程动态创建、运行、销毁;
- 并发性:多个进程可同时交替运行。
1.2 并发与并行区分
- 并发:单 CPU 核心,多个任务交替执行,宏观同时、微观串行;
- 并行:多 CPU 多核,多个任务同一时刻真正同时执行。
1.3 线程概述
线程是进程内部独立子任务,进程内调度执行最小单位,一个进程至少包含 1 个主线程,多个线程共享进程资源。多线程优势:
- 充分利用 CPU 多核,提升程序执行效率;
- 同一程序可同时处理多个独立任务(如下载 + 浏览)。
1.4 小结
进程:软件运行实例,分配内存资源;线程:进程内执行任务,调度单位;并发交替执行,并行多核同时执行;多线程提高程序运行效率。
二、Java 开启线程的三种方式
2.1 三种实现方式对比
- 继承
Thread类:无返回值,单继承有局限; - 实现
Runnable接口:无返回值,推荐,支持多实现、资源共享; - 实现
Callable接口:有返回值,搭配 FutureTask 获取线程执行结果。
2.2 方式 1:继承 Thread 类
public class MyThread extends Thread { @Override public void run() { // 线程执行逻辑 System.out.println("子线程运行"); } public static void main(String[] args) { MyThread t = new MyThread(); t.start(); // 开启线程,不能直接调用run() } }注意:调用start()才会创建新线程,直接调用 run 只是普通方法。
2.3 方式 2:实现 Runnable 接口(推荐)
public class TicketTask implements Runnable{ int ticket = 100; @Override public void run() { while(true){ if(ticket <= 0) break; System.out.println(Thread.currentThread().getName()+"卖出"+ticket); ticket--; } } public static void main(String[] args) { TicketTask task = new TicketTask(); new Thread(task,"窗口1").start(); new Thread(task,"窗口2").start(); new Thread(task,"窗口3").start(); } }优势:避免单继承限制,多个线程共用同一个任务对象,共享资源。
2.4 方式 3:实现 Callable(带返回值)
适用于线程执行后需要拿到运算结果场景,配合FutureTask包装任务交给 Thread。
2.5 小结
开发优先使用 Runnable;无返回简单任务用 Thread;需要线程结果使用 Callable。
三、线程相关方法
3.1 常用 API 表格
| 方法名称 | 说明 |
|---|---|
| String getName() | 获取线程名称 |
| void setName(String name) | 设置线程名,构造方法也可传参命名 |
| static Thread currentThread() | 获取当前正在执行的线程对象 |
| static void sleep(long ms) | 线程休眠,暂停指定毫秒,抛出异常 |
| setPriority(int) | 设置线程优先级,范围 1~5~10,默认 5 |
| int getPriority() | 获取当前线程优先级 |
| setDaemon(boolean) | 设置为守护线程(后台线程) |
3.2 线程调度方式
- 抢占式调度(Java 采用):CPU 优先执行高优先级线程,同优先级随机分配时间片;
- 非抢占式:线程主动让出 CPU,Java 不使用。
3.3 重点方法说明
- sleep:阻塞线程,时间结束自动就绪;
- 优先级仅代表获取 CPU 概率,无法保证绝对顺序;
- 守护线程:主线程结束后守护线程自动销毁,如 GC 垃圾回收线程。
3.4 小结
currentThread 获取当前线程;sleep 休眠;优先级影响调度概率;守护线程随主线程退出。
四、线程安全和同步
4.1 线程安全产生三大条件
- 多线程同时运行;
- 存在共享资源;
- 多条语句操作共享变量。卖票案例问题:多窗口同时访问 ticket,出现重复出票、负数票。
4.2 三种同步解决方案
4.2.1 同步代码块
语法:
synchronized(锁对象){ 多条操作共享数据代码 }要求:多个线程必须使用同一个锁对象,任意时刻仅一个线程进入代码块。
4.2.2 同步方法
方法上加synchronized修饰:
- 非静态同步方法:锁对象为
this; - 静态同步方法:锁对象为当前类字节码对象
类名.class。
4.2.3 Lock 锁(ReentrantLock)
手动加锁、手动释放,可读性更强:
Lock lock = new ReentrantLock(); lock.lock(); // 上锁 try{ // 操作共享资源 }finally{ lock.unlock(); // 必须释放锁,放在finally防止死锁 }4.3 同步优缺点
优点:解决多线程数据安全问题;缺点:同一时间单线程执行,程序运行效率下降。
4.4 小结
多线程共享变量会出现并发安全;同步代码块 / 同步方法 / Lock 三种锁机制;Lock 手动控制锁更灵活。
五、线程池介绍
5.1 线程池出现原因
频繁创建、销毁线程消耗大量操作系统资源,大量短时线程容易内存溢出。线程池提前缓存线程,复用线程对象,减少开销、控制并发数量。
5.2 线程池核心优势
- 复用线程,减少创建销毁开销;
- 控制并发线程总数,防止 CPU / 内存耗尽;
- 统一管理线程,方便监控。
5.3 JDK 内置 Executors 快捷创建(开发不推荐)
| 静态方法 | 作用 |
|---|---|
| newCachedThreadPool | 无上限弹性线程池,任务多会创建大量线程,易 OOM |
| newFixedThreadPool | 固定最大线程池,队列无上限,堆积任务内存溢出 |
阿里开发规范:禁止直接使用 Executors 创建线程池,必须手动ThreadPoolExecutor自定义。
5.4 小结
线程池复用线程提升性能;Executors 工具类存在 OOM 风险,生产环境不用。
六、自定义线程池 ThreadPoolExecutor
6.1 七参完整构造器
public ThreadPoolExecutor( int corePoolSize, // 核心线程数(正式员工) int maximumPoolSize, // 最大线程数=核心+临时线程 long keepAliveTime, // 临时线程空闲存活时间 TimeUnit unit, // 时间单位 BlockingQueue<Runnable> workQueue, // 任务阻塞队列 ThreadFactory threadFactory, // 线程工厂 RejectedExecutionHandler handler // 拒绝策略 )6.2 参数详解
- corePoolSize:常驻核心线程,无任务也不销毁;
- maximumPoolSize:最大并发线程;
- keepAliveTime:临时线程空闲多久自动销毁;
- workQueue:存放等待执行的任务;
- handler:核心 + 临时线程 + 队列全部占满,触发拒绝策略。
6.3 四大拒绝策略
| 策略 | 说明 |
|---|---|
| AbortPolicy(默认) | 直接抛出异常,阻断任务提交 |
| DiscardPolicy | 静默丢弃最新任务,无报错 |
| DiscardOldestPolicy | 丢弃队列等待最久任务,存入新任务 |
| CallerRunsPolicy | 提交任务的主线程执行该任务 |
6.4 线程池任务执行流程
- 任务数 ≤ 核心线程:直接分配核心线程执行;
- 核心线程已满,任务放入阻塞队列排队;
- 队列已满,创建临时线程直到达到最大线程数;
- 总线程 + 队列全部占满,执行拒绝策略。
6.5 小结
自定义线程池七个参数各司其职;掌握任务执行流程与四种拒绝策略,企业开发标准写法。
七、全文总结
- 进程是资源单位,线程是调度单位;并发交替、并行同时;
- 创建线程三种方式:Thread、Runnable(推荐)、Callable 带返回值;
- 线程 API:命名、休眠、优先级、守护线程;
- 多线程共享变量存在安全问题,三种同步锁:同步代码块、同步方法、Lock;
- 线程池复用线程降低开销,Executors 存在内存溢出隐患;
- ThreadPoolExecutor 七参自定义线程池,掌握任务执行流程与拒绝策略。
拓展学习建议
- 不加锁、加同步锁分别运行卖票案例,直观观察线程安全问题;
- 手写三种创建线程完整代码,区分有无返回值场景;
- 自定义线程池模拟任务过载,测试四种拒绝策略效果;
- 练习线程休眠、守护线程、优先级代码加深理解。
入门面试高频考点
- 进程和线程区别、并发并行区别;
- 创建线程三种方式优缺点;
- 线程安全问题产生条件,三种同步锁区别;
- Lock 与 synchronized 对比;
- 线程池作用,Executors 弊端,自定义线程池七大参数;
- 线程池任务执行流程、四种拒绝策略。
