【JAVA】多线程
一、基础概念
(一) 线程(Thread)
-
定义:程序内部的最小执行流程,是操作系统调度的基本单位。
-
单线程程序:仅1条执行流程(如默认main方法流程),代码依次执行。
示例:
public static void main(String[] args) {
// 单线程执行:依次执行循环逻辑
for(int i=8; i<10; i++){
System.out.println(i);
}
}
(二) 多线程
-
定义:从软硬件层面实现多条执行流程的技术,多条线程由CPU调度执行。
-
核心价值:
-
提升程序效率(如后台处理耗时任务,不阻塞主线程);
-
增强用户体验(如界面交互与数据加载并行)。
-
-
典型应用场景:
-
2306购票系统(并发处理购票请求);
-
消息通信、电商平台(订单处理、库存更新并行)。
-
二、Java线程创建方式
Java是通过 java.lang.Thread 类的对象来代表线程的。核心差异在于扩展性和是否支持返回结果。
| 创建方式 | 核心步骤 | 优点 | 缺点 |
|---|---|---|---|
| 继承Thread类 | 1. 子类继承Thread; 2. 重写run()(定义任务); 3. 创建对象 调用start()启动 |
编码简单 | 无返回结果、无法继承其他类、扩展性差; |
| 实现Runnable接口 | 1. 类实现Runnable; 2. 重写run(); 3. 创建任务对象; 4. 对象传入Thread构造器,调用start() |
扩展性强、可继承其他类、实现其他接口 | 无返回结果;需额外创建任务对象 |
| Callable+FutureTask | 1. 类实现Callable(T为返回值类型); 2. 重写call()(可抛异常); 3. 封装为线程任务对象FutureTask,传入Thread构造器; 4. 调用start()启动线程; 5. 用FutureTask.get()获取结果 |
扩展性强;支持返回任务结果 | 编码复杂、get()会阻塞 |
关键注意:仅start()能启动新线程(底层调用run());直接调用run()等同于普通方法,仍为单线程。
1. FutureTask核心API
| 构造器/方法 | 说明 |
|---|---|
| public FutureTask<>(Callable call) | 将Callable对象封装为FutureTask |
| public T get() throws Exception | 获取call()方法的返回结果(阻塞等待) |
2. 代码示例
// 1. 定义Callable任务类
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
// 线程任务:模拟耗时操作
Thread.sleep(1000);
return "线程执行完成!结果:" + Thread.currentThread().getName();
}
}
public class CallableTest {
public static void main(String[] args) throws Exception {
// 2. 封装为FutureTask
FutureTask<String> task = new FutureTask<>(new MyCallable());
// 3. 启动线程
new Thread(task).start();
// 4. 获取结果(阻塞直到任务完成)
String result = task.get();
System.out.println(result); // 输出:线程执行完成!结果:Thread-0
}
}
三、Thread类常用方法与构造器
| 构造器 | 说明 |
|---|---|
| public Thread(Runnable target) | 封装Runnable任务对象为Thread线程对象 |
| public Thread(String name) | 创建线程并指定线程名称 |
| public Thread(Runnable target, String name) | 封装任务对象并指定线程名称 |
| Thread 提供的常用方法 | 说明 |
|---|---|
| public void run() | 线程的任务方法 |
| public void start() | 启动线程 |
| public String getName() | 获取当前线程的名称,线程名称默认是 Thread-索引 |
| public void setName(String name) | 为线程设置名称 |
| public static Thread currentThread() | 获取当前执行的线程对象 |
| public static void sleep(long time) | 让当前执行的线程休眠指定毫秒后,再继续执行 |
| public final void join()… | 让调用该方法的线程先执行完毕 |
四、线程安全问题
多个线程同时操作同一共享资源且存在修改操作时,可能导致数据不一致或业务逻辑错误。
(一) 原因(三要素)
-
存在多个线程并发执行;
-
线程共享同一资源;
-
线程对共享资源有修改操作。
(二) 线程同步方案(解决线程安全)
核心原理:加锁机制,保证同一时刻仅1个线程访问共享资源(临界区)。
| 同步方式 | 核心语法/实现 | 锁对象规则 | 优缺点对比 |
|---|---|---|---|
| 同步代码块 | synchronized(锁对象) { 临界区代码 } |
多线程必须用同一把锁; 实例方法用 this,静态方法用 类名.class |
锁范围小(仅临界区),灵活性高;可读性稍差 |
| 同步方法 | 修饰符synchronized 返回值类型 方法名(形参列表) { 操作共享资源的核心代码 } |
实例方法:默认锁this 静态方法:默认锁 类名.class |
可读性好;锁范围大(整个方法体),灵活性低 |
| Lock锁(JDK5+) | 用ReentrantLock实现: 1. 创建锁对象Lock lock = new ReentrantLock(); 2. lock.lock()加锁; 3. finally中lock.unlock()释放 |
显式锁,无需默认锁对象 | 灵活性最高;需手动释放锁(避免死锁),编码稍复杂 |
五、线程通信
多个线程操作共享资源时,通过状态通知实现协调,避免无效资源争夺。
(一) 生产者-消费者模型
-
生产者线程:生产数据后,自身等待,通知消费者;
-
消费者线程:消费数据后,自身等待,通知生产者。
(二) 核心通信方法(Object类的方法)
需通过同步锁对象调用(与synchronized配合使用)。
| 方法 | 说明 |
|---|---|
| void wait() | 当前线程释放锁并进入无限等待,直到被其他线程notify()/notifyAll()唤醒 |
| void notify() | 唤醒当前锁对象上等待的单个线程 |
| void notifyAll() | 唤醒当前锁对象上等待的所有线程 |
六、线程池
定义:一种复用线程的技术(创建一批线程,反复使用,避免频繁创建/销毁的开销)。
无线程池的问题:
-
频繁创建线程消耗CPU/内存
-
大量线程会导致系统性能下降
(一) 工作原理

工作流程:
核心线程满 → 任务入队列 → 队列满 → 创建临时线程 → 临时线程满 → 触发拒绝策略
(二) 自定义线程池ThreadPoolExecutor
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数(常驻,不销毁)
int maximumPoolSize, // 最大线程数(核心+临时)
long keepAliveTime, // 临时线程空闲存活时间
TimeUnit unit, // 存活时间单位(如TimeUnit.SECONDS)
BlockingQueue<Runnable> workQueue, // 任务队列(队列满才创建临时线程)
ThreadFactory threadFactory, // 线程工厂(创建线程的工具,默认即可)
RejectedExecutionHandler handler // 任务拒绝策略(队列+最大线程满时触发)
)
临时线程创建时机与拒绝时机:
临时线程创建:核心线程满 → 任务队列满 → 创建临时线程;
拒绝任务:核心线程满 → 任务队列满 → 临时线程满 → 触发拒绝策略。
(三) 任务拒绝策略
| 策略 | 说明 |
|---|---|
| ThreadPoolExecutor.AbortPolicy | 丢弃任务并抛RejectedExecutionException(默认策略) |
| ThreadPoolExecutor.DiscardPolicy | 丢弃任务,不抛异常(不推荐) |
| ThreadPoolExecutor.DiscardOldestPolicy | 丢弃队列中等待最久的任务,加入新任务 |
| ThreadPoolExecutor.CallerRunsPolicy | 由提交任务的主线程直接执行任务(避免丢弃) |
(四) 核心接口 ExecutorService
| 方法 | 说明 |
|---|---|
| void execute(Runnable command) | 执行Runnable任务(无返回结果) |
| Future submit(Callable task) | 执行Callable任务(返回Future对象,用于获取结果) |
| void shutdown() | 等待所有任务执行完毕后关闭线程池(优雅关闭) |
| List shutdownNow() | 立即关闭线程池,停止正在执行的任务,返回未执行的任务 |
(五) 工具类 Executors(不推荐用于大型系统)
| 方法 | 说明 |
|---|---|
| Executors.newFixedThreadPool(int nThreads) | 创建固定线程数的线程池(核心线程=最大线程,无临时线程) |
| Executors.newSingleThreadExecutor() | 创建单线程线程池(仅1个核心线程) |
| Executors.newCachedThreadPool() | 可缓存线程池(核心线程=0,临时线程无上限,空闲60s回收) |
| Executors.newScheduledThreadPool(int corePoolSize) | 定时任务线程池(支持延迟/周期性执行) |
陷进:

七、并发与并行
| 类别 | 定义 | 核心特点 | 适用场景 |
|---|---|---|---|
| 并发(Concurrency) | CPU用时间片轮转调度线程,线程交替执行 | 同一时刻仅1个线程运行,用户感知“同时” | 单核CPU处理多线程 |
| 并行(Parallelism) | 多核CPU同时调度多个线程执行 | 真正“同时”运行,效率更高 | 多核CPU处理多线程 |
(一) 进程与线程的关系
-
进程:正在运行的程序,是操作系统资源分配的基本单位(拥有独立内存、文件句柄等);
-
线程:进程内的执行单元,是CPU调度的基本单位;
-
关联:一个进程可包含多个线程,线程共享进程的资源(内存、文件等),线程切换开销远小于进程。
八、线程生命周期
Java线程的6种状态定义在Thread.State枚举中,状态转换是线程从创建到销毁的完整过程。
| 状态 | 说明 |
|---|---|
| NEW(新建) | 线程对象已创建,但未调用start()(未启动) |
| RUNNABLE(可运行) | 线程已调用start(),等待CPU调度(包含“就绪”和“运行中”) |
| BLOCKED(锁阻塞) | 线程竞争锁失败,进入阻塞状态(获取锁后转为RUNNABLE) |
| WAITING(无限等待) | 线程调用wait()/join()等方法,释放锁并无限等待(需其他线程唤醒) |
| TIMED_WAITING(计时等待) | 线程调用sleep(long)/wait(long)等方法,释放锁并等待指定时间(时间到或被唤醒后转为RUNNABLE) |
| TERMINATED(被终止) | 线程run()方法执行完毕或因未捕获异常终止(生命周期结束,不可重启) |
核心状态转换流程
NEW →(start())→ RUNNABLE →(竞争锁失败)→ BLOCKED →(获取锁)→ RUNNABLE
RUNNABLE →(调用wait())→ WAITING →(notify()/notifyAll())→ RUNNABLE
RUNNABLE →(调用sleep(long))→ TIMED_WAITING →(时间到/notify())→ RUNNABLE
RUNNABLE →(run()执行完/异常)→ TERMINATED
更多推荐


所有评论(0)