Java基础全套教程(九)—— 多线程和并发编程详解
Java基础全套教程(九)—— 多线程和并发编程详解
在Java企业级开发中,单线程程序仅能串行执行任务,无法充分利用服务器CPU多核性能,面对高并发、多任务、异步处理场景(如接口批量请求、日志异步打印、订单异步处理、定时任务调度、文件批量解析)会出现性能瓶颈、响应超时等问题。
多线程与并发编程是Java高阶核心、面试必考重难点,也是后端开发进阶的必备技能。本章节将从零深度拆解多线程全套体系,包含核心概念、线程生命周期、线程创建方式、线程同步、锁机制、死锁原理与解决、线程通信、生产者消费者模式,全程搭配全新实战案例、底层原理剖析、企业避坑规范,贴合真实业务开发场景。
本章学习目标
-
彻底吃透程序、进程、线程的核心区别与关联,厘清并发、并行、串行的底层差异;
-
熟练掌握Java线程四大创建方式、核心优劣与适用场景,摒弃老旧写法,使用企业规范代码;
-
精通线程五大生命周期状态、状态切换底层机制,掌握线程核心API的使用原理;
-
深刻理解线程安全问题成因,熟练运用synchronized锁的三种使用方式与底层原理;
-
掌握多线程死锁的产生条件、排查思路与企业级解决方案;
-
吃透线程通信机制,掌握wait/notify/notifyAll核心方法的使用规范与坑点;
-
精通生产者消费者模式底层逻辑,理解线程协作核心思想,适配异步业务场景;
-
建立多线程开发规范,规避线程不安全、锁冗余、死锁、线程资源泄露等高频问题。
9.1 多线程核心基础概念
想要精通并发编程,首先必须厘清程序、进程、线程三者的层级关系,同时区分串行、并发、并行三大执行模式,这是所有多线程编程的基础,也是面试高频基础考点。
9.1.1 程序、进程、线程详解
1. 程序(Program)
程序是静态的代码集合,是一组有序的指令和数据的封装,对应磁盘上的可执行文件(如.exe、jar包),长期存储在磁盘中,不占用内存资源,不具备动态执行能力。简单来说,我们编写的Java代码、编译后的class文件、可运行jar包,都属于程序范畴。
2. 进程(Process)
进程是程序的一次动态执行过程,是操作系统分配系统资源(内存、CPU、文件句柄)的最小单位。程序启动运行后,就会转化为一个独立的进程,拥有独立的内存空间,进程之间相互隔离、互不干扰。
现代操作系统均支持多进程并发执行,例如一台电脑可以同时运行浏览器、编译器、微信、音乐播放器等多个程序,每个运行中的程序都是一个独立进程,操作系统通过资源调度实现多进程共存。
3. 线程(Thread)
线程是进程内部的最小执行单元,是操作系统CPU调度的基本单位,被包含在进程之中。一个进程默认包含一个主线程,同时可以创建多个子线程,同一进程下的所有线程共享进程的堆内存、方法区、系统资源,仅拥有独立的栈内存。
多线程的核心价值:将一个进程的复杂任务拆分多个子任务,由多个线程并行执行,充分利用CPU多核资源,大幅提升程序执行效率。
9.1.2 进程与线程核心区别(面试必背)
为了清晰区分二者,结合企业面试考点,总结核心差异如下:
-
资源归属不同:进程是资源分配最小单位,拥有独立内存;线程是调度执行最小单位,共享进程资源;
-
隔离性不同:进程之间完全隔离,互不影响;同一进程的线程共享资源,相互影响;
-
开销不同:进程创建、销毁、切换开销极大;线程开销极小,轻量化执行;
-
通信方式不同:进程间通信复杂(管道、套接字);线程间通信简单(共享变量、wait/notify);
-
生命周期不同:进程消亡则内部所有线程全部消亡;单个线程异常结束,不会影响主进程和其他线程。
9.1.3 串行、并发、并行核心区别
多线程编程的核心就是实现任务的高效执行,三种执行模式是理解并发的关键,三者无重叠、无混淆:
1. 串行(Serial)
所有任务按顺序依次执行,同一时间只有一个任务在运行,上一个任务执行完毕后,下一个任务才会开始,执行效率最低,单线程程序默认串行执行。
2. 并发(Concurrency)
单个CPU核心通过时间片轮询机制,快速交替执行多个线程任务。由于CPU切换速度极快,宏观上看似多个任务同时执行,微观上仍是串行执行。适用于任务数大于CPU核心数的场景,是Java多线程最常用的执行模式。
3. 并行(Parallelism)
多个CPU核心同时执行多个任务,真正意义上的同时运行,无任务切换开销,执行效率最高。仅适用于任务数小于等于CPU核心数的场景。
9.2 Java线程核心基础
9.2.1 主线程与子线程
1. 主线程(Main Thread)
Java程序启动时,JVM会自动创建一条主线程,专门执行main()方法代码,是程序的入口线程,也是所有子线程的父线程。
主线程核心特性:是程序最先执行的线程;不会等待子线程执行完毕,主线程结束后,程序不会立即终止,需等待所有非守护子线程执行完成;负责初始化程序环境、调度子线程任务。
2. 子线程
开发者在主线程中手动创建、启动的线程统称为子线程,用于分担主线程任务,实现多任务并行处理,提升程序运行效率。
9.2.2 线程核心常用API
Thread类是java.lang包下的核心线程类,提供了大量线程操作通用方法,是多线程编程的基础,核心API如下:
-
start():启动线程,将线程置入就绪状态,由JVM调度执行run()方法,仅可调用一次;
-
run():线程的核心执行体,线程调度成功后执行该方法内的业务代码;
3.Thread.sleep(long millis):静态方法,让当前线程休眠指定毫秒,进入阻塞状态,不释放锁资源;
-
currentThread():静态方法,获取当前正在执行的线程对象;
-
getName()/setName():获取/设置线程名称,方便日志排查与线程定位;
-
isAlive():判断线程是否处于活跃状态(就绪、运行、阻塞均为活跃);
-
yield():静态方法,让出当前CPU时间片,线程重回就绪状态,重新参与CPU调度;
-
join():等待目标线程执行完毕,当前线程再继续执行,实现线程顺序执行。
9.3 Java线程四大创建方式(企业级精讲)
Java提供四种线程创建方式,不同方式适配不同业务场景,其中前两种为基础方式,后两种为企业开发首选,本节提供全新实战案例,规避冗余代码,贴合开发规范。
9.3.1 方式一:继承Thread类
自定义线程类继承Thread类,重写run()方法,封装线程执行逻辑,通过实例化对象调用start()启动线程。
核心优劣:写法简单,但是存在单继承局限性,自定义类无法再继承其他业务父类,企业开发极少使用。
/**
* 继承Thread类实现多线程
* 场景:简单独立任务、无父类继承需求
*/
public class ThreadExtendDemo extends Thread {
// 重写线程执行体
@Override
public void run() {
// 获取当前线程名称,执行自定义任务
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " 执行任务,次数:" + i);
try {
// 线程休眠500ms,模拟业务耗时
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// 创建两个独立线程
ThreadExtendDemo task1 = new ThreadExtendDemo();
ThreadExtendDemo task2 = new ThreadExtendDemo();
// 设置线程名称,便于排查
task1.setName("任务线程-1");
task2.setName("任务线程-2");
// 启动线程
task1.start();
task2.start();
}
}
9.3.2 方式二:实现Runnable接口
自定义类实现Runnable接口,重写run()方法,将任务逻辑与线程对象解耦,突破单继承限制,是基础开发通用方式。
核心优势:支持多实现、无继承限制、任务可复用、多个线程可共享同一个任务对象。
/**
* 实现Runnable接口创建线程
* 场景:通用业务任务、需要保留类继承能力的场景
*/
public class RunnableImplDemo implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " 正在执行业务逻辑,次数:" + i);
try {
Thread.sleep(600);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// 单个任务对象,被多个线程共享
RunnableImplDemo businessTask = new RunnableImplDemo();
new Thread(businessTask, "业务线程-A").start();
new Thread(businessTask, "业务线程-B").start();
}
}
9.3.3 方式三:Callable+FutureTask(有返回值线程)
前两种方式线程执行后无返回值、无法抛出异常,Callable接口是高阶线程创建方式,支持线程任务返回结果、捕获执行异常,适配需要获取任务执行结果的业务场景。
核心优势:有返回值、可抛出异常、支持结果获取、可取消任务,企业异步查询、批量计算场景首选。
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/**
* Callable有返回值线程创建
* 场景:异步计算、接口异步查询、需要任务返回结果的场景
*/
public class CallableTaskDemo implements Callable<Integer> {
// 线程任务,支持返回值和异常抛出
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 10; i++) {
sum += i;
System.out.println(Thread.currentThread().getName() + " 累加计算:" + i);
Thread.sleep(300);
}
return sum;
}
public static void main(String[] args) {
// 封装Callable任务
FutureTask<Integer> futureTask = new FutureTask<>(new CallableTaskDemo());
// 启动线程
new Thread(futureTask, "计算线程").start();
try {
// 获取线程执行返回结果,会阻塞主线程,直到任务执行完毕
Integer result = futureTask.get();
System.out.println("线程计算最终结果:" + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
9.3.4 方式四:线程池创建(企业终极首选)
手动创建、销毁线程开销极大,频繁创建线程会造成资源浪费、程序卡顿。线程池可复用线程、控制并发数量、统一管理线程生命周期,是企业生产环境唯一推荐使用的线程创建方式。
核心优势:线程复用、减少资源开销、控制并发量、避免线程泛滥、任务统一调度管理。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池创建线程(企业规范写法)
* 场景:所有生产环境多任务场景、批量任务、异步任务
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
// 创建固定容量线程池,核心线程数3
ExecutorService threadPool = Executors.newFixedThreadPool(3);
// 批量提交10个任务,线程池自动调度执行
for (int i = 1; i <= 10; i++) {
int taskNum = i;
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 执行第" + taskNum + "号任务");
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 关闭线程池
threadPool.shutdown();
}
}
9.3.5 四种创建方式核心选型总结
-
继承Thread类:仅用于测试、简单demo,生产环境禁用;
-
实现Runnable接口:适用于无返回值、简单异步任务,少量临时任务使用;
-
Callable+FutureTask:适用于需要获取任务返回结果、捕获执行异常的异步任务;
-
线程池:生产环境所有多线程场景首选,统一规范、性能最优。
9.4 线程生命周期与状态切换
线程从创建到销毁的完整过程,称为线程生命周期。Java线程在运行过程中会经历五大核心状态,状态的切换是多线程调度、线程安全、死锁问题的底层核心,是面试高频重难点。
9.4.1 线程五大状态详解
1. 新生状态(New)
通过new关键字创建线程对象后,线程进入新生状态。此时仅完成对象初始化,分配栈内存,未调用start()方法,无CPU执行权限,属于纯静态状态。
2. 就绪状态(Runnable)
调用start()方法后,线程进入就绪状态。该状态下线程具备所有执行条件,等待JVM线程调度器分配CPU时间片,是进入运行状态的前置状态。
除了新建线程启动,阻塞解除、yield()方法执行、CPU时间片切换后,线程都会重回就绪状态。
3. 运行状态(Running)
就绪状态的线程获取到CPU时间片后,进入运行状态,执行run()/call()方法的业务代码。该状态是线程唯一执行任务的状态,时间片耗尽后会退回就绪状态。
4. 阻塞状态(Blocked)
线程运行过程中,因等待资源、休眠、同步锁、IO阻塞等原因,主动暂停执行,释放CPU时间片,进入阻塞状态。阻塞状态下线程不会参与CPU调度,阻塞条件解除后,自动重回就绪状态。
常见阻塞场景:sleep()休眠、wait()等待、同步锁等待、阻塞IO操作。
5. 死亡状态(Terminated)
线程任务执行完毕、异常终止、主动终止后,进入死亡状态,线程生命周期结束,释放所有线程资源,无法再次启动。
9.4.2 线程状态切换核心规则
-
新生状态只能单向切换为就绪状态,无法直接运行或阻塞;
-
运行状态可切换为就绪、阻塞、死亡三种状态;
-
阻塞状态只能单向切换为就绪状态,无法直接运行;
-
线程死亡后彻底销毁,不可重复调用start()启动。
9.5 线程安全与同步机制(核心重难点)
9.5.1 线程安全问题成因
多线程场景下,多个线程同时操作同一个共享资源,由于线程抢占式调度、CPU时间片随机切换,会导致数据读写错乱、数据覆盖、数据不一致等问题,即为线程不安全问题。
核心产生条件:多线程环境、共享资源、非原子操作,三者同时满足必然出现线程安全问题。
9.5.2 synchronized同步锁详解
synchronized是Java原生内置的同步锁机制,可保证代码块/方法的原子性、可见性、有序性,彻底解决多线程共享资源安全问题,是企业开发最常用的线程同步方案。
synchronized共有三种使用方式,锁的范围从小到大,适配不同业务场景。
1. 同步代码块(锁对象)
锁定指定对象,仅锁住核心业务代码,锁范围最小、性能最优,企业开发首选。同一对象的同步代码块,同一时间仅允许一个线程执行。
/**
* 同步代码块(对象锁)解决线程安全问题
* 场景:仅部分代码操作共享资源,缩小锁范围,提升性能
*/
public class SyncObjectLockDemo {
// 共享资源:票数
private static int ticket = 10;
// 自定义锁对象(唯一锁)
private final static Object LOCK = new Object();
public static void main(String[] args) {
// 三个线程同时抢票
new Thread(() -> sellTicket(), "窗口1").start();
new Thread(() -> sellTicket(), "窗口2").start();
new Thread(() -> sellTicket(), "窗口3").start();
}
// 售票业务
private static void sellTicket() {
while (true) {
// 同步代码块:锁定唯一对象
synchronized (LOCK) {
if (ticket <= 0) {
System.out.println(Thread.currentThread().getName() + ":售票结束,无剩余票数");
break;
}
// 模拟售票耗时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName() + " 售票成功,剩余票数:" + ticket);
}
}
}
}
2. 同步实例方法(锁当前对象this)
修饰普通成员方法,默认锁定当前实例对象this,同一对象的所有同步实例方法互斥,适用于对象级别的资源同步场景。
/**
* 同步实例方法(this对象锁)
* 场景:实例方法操作对象内共享资源
*/
public class SyncMethodLockDemo {
private int count = 0;
// 同步实例方法,锁当前对象this
public synchronized void increment() {
count++;
System.out.println(Thread.currentThread().getName() + " 计数+1,当前值:" + count);
}
public static void main(String[] args) {
SyncMethodLockDemo demo = new SyncMethodLockDemo();
// 多线程调用同一对象同步方法
new Thread(demo::increment, "线程-X").start();
new Thread(demo::increment, "线程-Y").start();
}
}
3. 同步静态方法(锁类Class对象)
修饰静态方法,锁定当前类的Class对象,全局唯一锁,所有该类的实例对象共享同一把锁,适用于静态共享资源同步场景。
/**
* 同步静态方法(类锁)
* 场景:静态共享资源、全局同步场景
*/
public class SyncStaticLockDemo {
// 静态共享资源
private static int score = 0;
// 同步静态方法,锁当前类Class对象
public static synchronized void addScore() {
score += 10;
System.out.println(Thread.currentThread().getName() + " 加分成功,当前总分:" + score);
}
public static void main(String[] args) {
// 多个实例对象,共享类锁
new Thread(SyncStaticLockDemo::addScore, "学生线程1").start();
new Thread(SyncStaticLockDemo::addScore, "学生线程2").start();
}
}
9.5.3 锁核心规则总结
-
对象锁:锁当前实例对象,不同实例对象之间锁互不干扰;
-
类锁:锁Class对象,全局唯一,所有实例对象共享锁,优先级最高;
-
同步代码块性能优于同步方法,企业开发优先缩小锁范围,避免锁冗余;
-
线程获取锁后执行代码,执行完毕自动释放锁,其他线程竞争获取锁。
9.6 死锁原理与企业级解决方案
死锁是多线程开发中高频疑难问题,指多个线程互相持有对方需要的锁资源,同时互相等待对方释放锁,导致所有线程永久阻塞、程序卡死,无法继续执行。
9.6.1 死锁四大必要条件(必考)
死锁产生必须同时满足四个条件,缺一不可,解决死锁只需破坏任意一个条件即可:
-
互斥条件:锁资源同一时间仅能被一个线程持有;
-
请求与保持条件:线程持有当前锁,同时请求获取其他锁资源;
-
不可剥夺条件:已持有的锁资源,不会被其他线程强制剥夺;
-
循环等待条件:多个线程形成循环锁等待链路。
9.6.2 死锁实战案例演示
/**
* 死锁案例演示
* 场景:两个线程互相持有对方所需锁资源,循环等待
*/
public class DeadLockDemo {
// 定义两个全局锁资源
private static final String LOCK_A = "资源锁A";
private static final String LOCK_B = "资源锁B";
public static void main(String[] args) {
// 线程1:持有锁A,请求锁B
new Thread(() -> {
synchronized (LOCK_A) {
System.out.println("线程1 获取到锁A,等待锁B");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 循环等待锁B,形成死锁
synchronized (LOCK_B) {
System.out.println("线程1 同时获取锁A、锁B");
}
}
}, "死锁线程-1").start();
// 线程2:持有锁B,请求锁A
new Thread(() -> {
synchronized (LOCK_B) {
System.out.println("线程2 获取到锁B,等待锁A");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 循环等待锁A,形成死锁
synchronized (LOCK_A) {
System.out.println("线程2 同时获取锁A、锁B");
}
}
}, "死锁线程-2").start();
}
}
9.6.3 死锁企业级解决方案
结合死锁四大条件,生产环境核心解决方案如下,优先级从高到低:
-
破坏循环等待条件(首选):统一所有线程的锁获取顺序,所有线程先获取锁A、再获取锁B,杜绝循环等待链路;
-
缩小锁范围:禁止在同一个同步代码块中嵌套多把锁,单个代码块只持有一把锁;
-
设置锁超时时间:线程等待锁超时后自动释放已持有资源,避免永久阻塞;
-
避免锁嵌套:业务代码分层设计,杜绝多层同步锁嵌套调用。
/**
* 死锁解决方案:统一锁获取顺序
* 核心:所有线程锁获取顺序一致,破坏循环等待条件
*/
public class DeadLockResolveDemo {
private static final String LOCK_A = "资源锁A";
private static final String LOCK_B = "资源锁B";
public static void main(String[] args) {
// 线程1:先A后B
new Thread(() -> {
synchronized (LOCK_A) {
System.out.println("线程1 获取锁A");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (LOCK_B) {
System.out.println("线程1 获取锁B,执行完毕");
}
}
}, "线程-1").start();
// 线程2:统一先A后B,杜绝循环等待
new Thread(() -> {
synchronized (LOCK_A) {
System.out.println("线程2 获取锁A");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (LOCK_B) {
System.out.println("线程2 获取锁B,执行完毕");
}
}
}, "线程-2").start();
}
}
9.7 线程通信机制(wait/notify)
多线程除了独立执行任务、竞争资源外,还需要线程间协作通信,实现任务联动、顺序执行、资源调度。Java通过Object类提供的wait()、notify()、notifyAll()三大方法实现线程通信。
9.7.1 核心通信方法详解
-
wait():让当前持有锁的线程释放锁资源,进入无限等待阻塞状态,等待其他线程唤醒;
-
notify():随机唤醒一个处于当前对象等待池的线程;
-
notifyAll():唤醒当前对象等待池的所有等待线程;
强制规范:三大方法必须在同步代码块/同步方法中调用,且必须是当前锁对象调用,否则抛出异常。
核心区别:sleep()休眠不释放锁,wait()等待主动释放锁,避免其他线程阻塞。
9.7.2 线程通信核心场景
线程通信核心用于多线程任务协作,解决线程忙等、资源抢占混乱问题,最经典的落地场景就是生产者消费者模式。
9.8 生产者消费者模式(并发核心设计模式)
生产者消费者模式是多线程并发编程的核心设计模式,用于解决生产者线程与消费者线程的任务协作、资源解耦、忙闲不均问题,广泛应用于消息队列、异步处理、日志落地、任务调度等企业场景。
9.8.1 模式核心角色
1.生产者线程:负责生产数据、生成任务,将数据存入缓冲区;
-
消费者线程:负责从缓冲区获取数据、处理任务;
-
缓冲区:线程共享的中间容器,解耦生产者与消费者,平衡生产消费速度。
9.8.2 企业级实战实现
本次采用任务队列作为缓冲区,通过wait/notify实现线程精准通信,解决生产溢出、消费为空问题,代码简洁规范、贴合生产环境。
import java.util.LinkedList;
import java.util.Queue;
/**
* 生产者消费者模式实战(企业规范版)
* 核心:线程通信、资源解耦、平衡生产消费速度
*/
public class ProducerConsumerDemo {
// 定义任务缓冲区队列,最大容量5
private static final Queue<String> TASK_QUEUE = new LinkedList<>();
private static final int MAX_SIZE = 5;
// 全局锁对象
private static final Object LOCK = new Object();
// 生产者线程:生产任务存入队列
static class Producer implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
synchronized (LOCK) {
// 缓冲区满,生产者等待
while (TASK_QUEUE.size() >= MAX_SIZE) {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 生产任务
String task = "业务任务-" + i;
TASK_QUEUE.add(task);
System.out.println("生产者完成生产:" + task + ",当前队列任务数:" + TASK_QUEUE.size());
// 唤醒消费者线程
LOCK.notifyAll();
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 消费者线程:从队列获取任务消费
static class Consumer implements Runnable {
@Override
public void run() {
while (true) {
synchronized (LOCK) {
// 缓冲区为空,消费者等待
while (TASK_QUEUE.isEmpty()) {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 消费任务
String task = TASK_QUEUE.poll();
System.out.println("消费者完成处理:" + task + ",剩余任务数:" + TASK_QUEUE.size());
// 唤醒生产者线程
LOCK.notifyAll();
}
try {
Thread.sleep(600);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
// 启动生产者、消费者线程
new Thread(new Producer(), "生产者线程").start();
new Thread(new Consumer(), "消费者线程").start();
}
}
9.8.3 模式核心价值与总结
-
解耦线程:生产者与消费者不直接交互,通过缓冲区通信,代码解耦、拓展性更强;
-
平衡效率:解决生产快、消费慢或生产慢、消费快的忙闲不均问题,提升整体吞吐量;
-
线程协作:通过wait/notify实现精准线程通信,避免空循环、资源空耗;
-
高并发适配:是消息队列、线程池、异步框架的底层核心思想,中高级开发必备。
9.9 本章高频坑点与开发规范汇总
1. 线程创建坑点
-
禁止生产环境手动频繁创建线程,必须使用线程池统一管理;
-
start()方法仅可调用一次,重复调用抛出线程状态异常;
-
区分run()与start():直接调用run()只是普通方法执行,不会创建新线程。
2. 锁机制坑点
-
同步锁尽量缩小范围,避免全局大锁导致程序串行、性能降级;
-
区分对象锁与类锁,不同锁类型作用范围不同,避免锁失效导致线程不安全;
-
禁止锁嵌套,极易引发死锁问题。
3. 线程通信坑点
-
wait/notify/notifyAll必须在同步代码块中执行,否则抛出非法监视器异常;
-
必须使用while循环判断等待条件,避免虚假唤醒问题;
-
sleep()不释放锁,wait()释放锁,二者核心区别必须厘清。
4. 死锁避坑规范
-
统一多锁获取顺序,杜绝循环等待;
-
单个代码块只持有一把锁,禁止多层锁嵌套;
-
复杂并发场景优先使用成熟工具类,避免手写锁逻辑。
9.10 本章核心知识点终极总结
-
程序是静态代码,进程是资源分配最小单位,线程是CPU调度最小单位,同一进程线程共享资源、相互协作;
-
并发是CPU时间片交替执行,并行是多核同时执行,串行是顺序执行,多线程核心提升并发处理能力;
-
线程四种创建方式各有优劣,线程池是生产环境唯一规范写法,支持线程复用、资源管控;
-
线程包含新生、就绪、运行、阻塞、死亡五大状态,状态切换是线程调度的底层核心;
-
synchronized通过对象锁、类锁实现线程同步,解决多线程共享资源安全问题,优先使用小范围同步代码块;
-
死锁需满足四大条件,生产环境通过统一锁顺序、杜绝锁嵌套彻底规避;
-
wait/notify实现线程通信,生产者消费者模式是线程协作的核心落地场景,是高并发异步编程的基础;
-
多线程开发核心规范:慎用手动线程、缩小锁范围、规避死锁、合理使用线程通信,兼顾性能与线程安全。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)