跳到主要内容Java 多线程核心:线程与进程区别、创建方式及线程安全 | 极客日志Javajava算法
Java 多线程核心:线程与进程区别、创建方式及线程安全
Java 线程与进程的区别,涵盖 Thread、Runnable、Callable 及线程池的创建与管理,重点讲解 synchronized、Lock、volatile 等线程安全保障方案,并分析死锁、竞态条件等常见问题及高并发系统设计原则。
雾岛听风7 浏览 一、基础核心:线程与进程的区别
在理解并发编程前,首先要明确「进程」和「线程」的核心概念与差异,这是后续所有内容的基础。
1. 核心定义
- 进程:操作系统进行资源分配和调度的最小单位(资源包括内存、CPU、磁盘 IO 等),每个进程拥有独立的内存空间、文件句柄等资源,进程之间相互隔离,通信成本高(如管道、Socket、共享内存)。
示例:你打开的微信、IDEA、浏览器,各自都是一个独立进程。
- 线程:进程内的执行单元和调度的最小单位(也叫「轻量级进程」),一个进程可以包含多个线程,所有线程共享该进程的资源(内存、文件句柄等),线程之间通信成本低(直接操作共享变量)。
示例:微信进程中,接收消息、发送消息、刷新朋友圈,各自对应一个独立线程。
2. 通俗比喻
把「进程」比作一个工厂,工厂有独立的厂房、设备、原材料(对应进程的独立资源);
把「线程」比作工厂里的工人,多个工人共享工厂的所有资源,协同完成生产任务,工人之间沟通(线程通信)比工厂之间(进程通信)更便捷。
3. 核心区别总结
| 对比维度 | 进程 | 线程 |
|---|
| 资源分配 | 操作系统分配资源的最小单位 | 共享所属进程的资源,无独立资源 |
| 内存空间 | 独立的地址空间,互不干扰 | 共享进程的堆、方法区,私有程序计数器、虚拟机栈 |
| 通信成本 | 高(需依赖操作系统提供的 IPC 机制) | 低(直接操作共享变量,需保证线程安全) |
| 创建/销毁开销 | 大(需分配/释放独立资源) | 小(仅需创建执行栈等少量资源) |
| 调度成本 | 高(由操作系统内核调度) | 低(可由 JVM 自主调度,部分依赖内核) |
| 健壮性 | 高(一个进程崩溃不影响其他进程) | 低(一个线程崩溃可能导致整个进程崩溃) |
二、Java 中线程的创建与管理
Java 提供了多种线程创建方式,从基础的 Thread/Runnable,到带返回值的 Callable,再到企业级推荐的「线程池」,性能和灵活性逐步提升。
1. 基础创建方式(无返回值)
方式 1:继承 Thread 类
- 步骤:继承
java.lang.Thread 类,重写 run() 方法(线程执行逻辑),调用 start() 方法启动线程。
- 注意:启动线程必须调用
start(),而非直接调用 run()(start() 会触发 JVM 创建新线程并执行 run(),直接调用 run() 只是普通方法调用,不会创建新线程)。
- 弊端:Java 是单继承机制,继承
Thread 后无法再继承其他类,灵活性受限。
class MyThread extends Thread {
{
( ; i < ; i++) {
System.out.println(Thread.currentThread().getName() + + i);
}
}
}
{
{
();
();
thread1.setName();
thread2.setName();
thread1.start();
thread2.start();
}
}
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
@Override
public
void
run
()
for
int
i
=
0
5
":执行中,i="
public
class
ThreadDemo
public
static
void
main
(String[] args)
MyThread
thread1
=
new
MyThread
MyThread
thread2
=
new
MyThread
"线程 1"
"线程 2"
方式 2:实现 Runnable 接口
- 步骤:实现
java.lang.Runnable 接口,重写 run() 方法,将 Runnable 实例传入 Thread 构造器,调用 Thread 的 start() 方法启动。
- 优势:规避单继承限制,可同时实现其他接口;
Runnable 只封装执行逻辑,线程对象与执行逻辑分离,更易解耦和资源共享。
- 这是比继承
Thread 更推荐的基础创建方式。
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ":执行中,i=" + i);
}
}
}
public class RunnableDemo {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread1 = new Thread(runnable, "线程 1");
Thread thread2 = new Thread(runnable, "线程 2");
thread1.start();
thread2.start();
}
}
2. 进阶创建方式(带返回值、可抛异常):Callable + FutureTask
上述两种基础方式的 run() 方法无返回值,且无法抛出受检异常(只能捕获处理)。Callable 接口弥补了这一缺陷,适合需要获取线程执行结果的场景。
- 核心组件:
Callable<V>:接口,只有一个 call() 方法,返回泛型 V,可抛出受检异常。
FutureTask<V>:实现了 Runnable 和 Future<V> 接口,封装 Callable 实例,既可以作为 Thread 的构造参数,又可以通过 get() 方法获取 call() 的返回值。
- 步骤:实现
Callable 接口→创建 FutureTask 封装 Callable→创建 Thread 封装 FutureTask→启动线程→调用 FutureTask.get() 获取返回值(会阻塞当前线程,直到子线程执行完成)。
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class MyCallable implements Callable<Integer> {
private int num;
public MyCallable(int num) {
this.num = num;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= num; i++) {
sum += i;
}
return sum;
}
}
public class CallableDemo {
public static void main(String[] args) throws Exception {
MyCallable callable = new MyCallable(100);
FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask, "求和线程").start();
Integer result = futureTask.get();
System.out.println("1 到 100 的和:" + result);
}
}
3. 企业级推荐:线程池(ThreadPoolExecutor)
手动创建线程(Thread/Runnable/Callable)存在诸多问题:频繁创建销毁线程开销大、线程数量无法控制(可能导致 OOM)、无法统一管理线程生命周期。
线程池是一种「池化技术」,提前创建一批线程并缓存,任务到来时直接分配线程执行,任务完成后线程不销毁,返回线程池等待下一个任务,完美解决上述问题,是高并发场景下的首选。
3.1 核心组件:ThreadPoolExecutor
Java 并发包 java.util.concurrent(JUC)提供了 ThreadPoolExecutor,这是线程池的核心实现类,推荐手动创建 ThreadPoolExecutor(而非使用 Executors 静态方法,避免资源耗尽风险)。
(1)ThreadPoolExecutor 七大核心参数(必须掌握)
public ThreadPoolExecutor(int corePoolSize, // 1. 核心线程数(常驻线程数,即使空闲也不会销毁,除非设置 allowCoreThreadTimeOut=true)
int maximumPoolSize, // 2. 最大线程数(线程池能容纳的最大线程总数,核心线程 + 非核心线程)
long keepAliveTime, // 3. 非核心线程空闲超时时间(超时后销毁非核心线程)
TimeUnit unit, // 4. keepAliveTime 的时间单位(如 TimeUnit.SECONDS)
BlockingQueue<Runnable> workQueue, // 5. 任务阻塞队列(核心线程忙时,新任务存入该队列等待)
ThreadFactory threadFactory, // 6. 线程工厂(用于创建线程,可自定义线程名称、优先级等)
RejectedExecutionHandler handler)
(2)四大拒绝策略(JUC 内置)
AbortPolicy(默认):直接抛出 RejectedExecutionException 异常,拒绝新任务。
CallerRunsPolicy:由提交任务的线程(如主线程)自己执行该任务,减缓任务提交速度。
DiscardPolicy:直接丢弃新任务,不抛出异常,无任何提示。
DiscardOldestPolicy:丢弃队列中最旧的任务,然后尝试提交新任务。
(3)线程池核心运行流程
- 新任务提交,首先判断核心线程数是否已满(当前运行线程数 < corePoolSize):是则创建核心线程执行任务,否则进入下一步。
- 判断任务队列是否已满:未满则将任务存入队列,否则进入下一步。
- 判断最大线程数是否已满(当前运行线程数 < maximumPoolSize):是则创建非核心线程执行任务,否则进入下一步。
- 执行拒绝策略,处理无法接收的新任务。
3.2 实战:手动创建 ThreadPoolExecutor
import java.util.concurrent.*;
public class ThreadPoolDemo {
public static void main(String[] args) {
int corePoolSize = 2;
int maximumPoolSize = 5;
long keepAliveTime = 30;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler
);
for (int i = 1; i <= 20; i++) {
int taskNum = i;
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + ":执行任务" + taskNum);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
threadPool.shutdown();
}
}
3.3 注意事项
- 避免使用
Executors 静态方法(newFixedThreadPool、newCachedThreadPool 等):newFixedThreadPool 和 newSingleThreadExecutor 的任务队列是无界的,可能导致 OOM;newCachedThreadPool 的最大线程数是 Integer.MAX_VALUE,可能创建大量线程导致 OOM。
- 合理配置参数:核心线程数可根据 CPU 核心数(
Runtime.getRuntime().availableProcessors())调整,IO 密集型任务可设置较大值,CPU 密集型任务可设置较小值(如 CPU 核心数 +1)。
- 必须关闭线程池:避免线程泄漏,常用
shutdown()(平缓关闭)和 shutdownNow()(强制关闭,中断正在执行的任务)。
三、多线程环境下的线程安全保障
线程安全的核心是解决原子性、可见性、有序性三大问题(这是并发编程的基石),Java 提供了多种解决方案,从轻量到重量,从无锁到有锁,适用于不同场景。
1. 先明确:三大核心问题
- 原子性:操作不可分割,要么全部执行完成且结果不被干扰,要么不执行。示例:
i++(分为读取、加 1、写入三步,非原子操作,多线程下会出现数据错乱)。
- 可见性:一个线程修改了共享变量的值,其他线程能立刻看到修改后的最新值。示例:线程 A 修改了变量
flag=true,线程 B 可能仍读取到 flag=false(因为 JVM 的内存缓存机制,线程 A 修改的是工作内存中的值,未及时刷新到主内存)。
- 有序性:程序执行顺序按照代码的先后顺序执行,避免指令重排。示例:单例模式的双重检查锁,若不做处理,可能出现指令重排导致的空指针异常。
2. 解决方案汇总
方案 1:volatile(轻量级,解决可见性、有序性,不解决原子性)
volatile 是一个关键字,用于修饰共享变量,是最轻量级的线程安全保障方案,无锁开销,性能优异。
- 核心原理:
- 保证可见性:线程修改
volatile 变量后,会立即刷新到主内存;线程读取 volatile 变量时,会直接从主内存读取,跳过工作内存缓存。
- 保证有序性:禁止 JVM 对
volatile 变量相关的指令进行重排(通过内存屏障实现)。
- 不保证原子性:
volatile 无法将多步操作变为不可分割的原子操作,示例:volatile int i=0; i++ 仍会出现数据错乱。
- 适用场景:
- 状态标记位(如停止线程的
flag)。
- 双重检查锁的单例模式(修饰实例变量,避免指令重排)。
- 示例代码(状态标记位):
class VolatileDemo implements Runnable {
private volatile boolean stop = false;
@Override
public void run() {
int i = 0;
while (!stop) {
i++;
}
System.out.println("线程停止,i=" + i);
}
public void stopThread() {
stop = true;
}
}
public class VolatileTest {
public static void main(String[] args) throws InterruptedException {
VolatileDemo demo = new VolatileDemo();
Thread thread = new Thread(demo, "工作线程");
thread.start();
Thread.sleep(1000);
demo.stopThread();
}
}
方案 2:synchronized(JVM 内置悲观锁,解决原子性、可见性、有序性)
synchronized 是 Java 内置的同步锁,属于「可重入悲观锁」,使用简单,无需手动管理锁的释放(即使发生异常,JVM 也会自动释放锁)。
- 核心原理:
- 保证原子性:同一时间只有一个线程能进入同步代码块/方法,将多步操作变为原子操作。
- 保证可见性:线程释放锁时,会将工作内存中的修改刷新到主内存;线程获取锁时,会从主内存读取最新值到工作内存。
- 保证有序性:同步代码块内的指令禁止重排。
- 优化:现代 JVM 对
synchronized 做了「锁升级」优化(偏向锁→轻量级锁→重量级锁),低并发下性能接近无锁方案,只有高并发竞争激烈时才升级为重量级锁(依赖操作系统内核,有阻塞开销)。
- 用法(两种):
- 同步方法:锁对象是「当前实例」(非静态方法)或「类对象」(静态方法)。
- 同步代码块:锁对象可自定义(推荐,缩小锁粒度,提升性能)。
- 示例代码(解决
i++ 原子性问题):
class SynchronizedCounter {
private int count = 0;
public synchronized void increment1() {
count++;
}
public void increment2() {
synchronized (this) {
count++;
}
}
public synchronized int getCount() {
return count;
}
}
方案 3:Lock 接口(显式悲观锁,解决原子性、可见性、有序性)
Lock 是 JUC 提供的显式锁接口,核心实现类是 ReentrantLock(可重入锁),比 synchronized 更灵活,支持超时获取锁、可中断锁、公平锁等功能。
- 核心优势:
- 手动控制锁的获取和释放(必须在
finally 块中释放锁,避免锁泄露)。
- 支持
tryLock(long timeout, TimeUnit unit):超时获取锁,避免死锁。
- 支持
lockInterruptibly():可中断等待锁的线程。
- 支持公平锁(构造参数传入
true):按照线程等待顺序获取锁,避免饥饿,但性能略低。
- 示例代码(解决
i++ 原子性问题,避免死锁):
import java.util.concurrent.locks.ReentrantLock;
class LockCounter {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
public void increment() {
boolean isLocked = false;
try {
isLocked = lock.tryLock(1, TimeUnit.SECONDS);
if (isLocked) {
count++;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (isLocked) {
lock.unlock();
}
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
方案 4:CAS(无锁乐观锁,解决原子性、可见性、有序性)
CAS(Compare-And-Swap,比较并交换)是一种无锁算法,由 CPU 硬件提供原子指令支持,属于「乐观锁」,核心思想是「假设无竞争,直接尝试修改,失败则重试」。
- 核心原理(三个操作数):
- 内存值
V:共享变量的当前主内存值。
- 预期值
A:线程读取到的共享变量初始值(工作内存值)。
- 更新值
B:线程要修改后的目标值。
执行逻辑:当且仅当 V == A 时,CPU 将 V 更新为 B;否则不做任何操作,线程自旋重试(或放弃)。
- Java 中的封装:JUC 的
java.util.concurrent.atomic 包(如 AtomicInteger、AtomicLong),底层封装了 CAS 操作,无需手动实现。
- 注意事项:存在「ABA 问题」(变量被修改为 B 后又改回 A,CAS 误认为未被修改),可通过
AtomicStampedReference(添加版本号)解决。
- 示例代码(无锁解决
i++ 问题):
import java.util.concurrent.atomic.AtomicInteger;
class AtomicCounter {
private final AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.getAndIncrement();
}
public int getCount() {
return count.get();
}
}
3. 方案选型对比
| 方案 | 解决的问题 | 锁类型 | 性能 | 灵活性 | 适用场景 |
|---|
volatile | 可见性、有序性(不解决原子性) | 无锁 | 最优 | 低 | 状态标记位、双重检查锁单例 |
synchronized | 原子性、可见性、有序性 | 悲观锁(JVM 内置) | 较好(锁升级优化) | 低 | 低到中并发、简单场景、无需复杂锁操作 |
ReentrantLock | 原子性、可见性、有序性 | 悲观锁(显式) | 较好 | 高 | 中到高并发、复杂场景(超时、中断、公平锁) |
CAS/Atomic | 原子性、可见性、有序性 | 乐观锁(无锁) | 优秀 | 中 | 高并发、简单变量修改(int/long/引用) |
四、常见并发问题的规避与解决
高并发开发中,容易遇到各类并发问题,掌握其产生原因和解决方案,是保证系统稳定的关键。
1. 死锁
- 定义:多个线程互相持有对方需要的锁,且都不释放自己的锁,导致所有线程无限阻塞。
- 产生的四个必要条件(缺一不可):互斥条件、请求与保持条件、不可剥夺条件、循环等待条件。
- 解决方案:
- 统一锁的获取顺序(破坏循环等待条件,最常用)。
- 避免锁的嵌套使用(减少请求与保持条件)。
- 使用
ReentrantLock 的 tryLock() 超时获取锁(破坏不可剥夺条件)。
- 减少锁的持有时间,只在核心逻辑加锁。
2. 竞态条件
- 定义:多个线程同时修改共享资源,最终结果依赖线程的执行顺序,导致数据错乱。
- 示例:
i++、多线程修改同一个 HashMap。
- 解决方案:
- 加锁(
synchronized/ReentrantLock)。
- 使用无锁 CAS(
Atomic 系列)。
- 使用线程安全的并发容器(如
ConcurrentHashMap)。
3. 内存可见性问题
- 定义:一个线程修改共享变量后,其他线程无法立即看到最新值,导致逻辑错误。
- 产生原因:JVM 的工作内存与主内存的缓存机制。
- 解决方案:
- 用
volatile 修饰共享变量。
- 用
synchronized/Lock 加锁(保证释放锁时刷新到主内存,获取锁时读取主内存)。
4. 线程泄漏
- 定义:线程创建后无法正常结束,长期占用系统资源,最终导致资源耗尽。
- 产生原因:线程执行无限循环、异常未捕获导致线程终止但资源未释放、线程池核心线程无法回收。
- 解决方案:
- 避免线程内无限循环,设置合理的退出条件。
- 捕获线程内所有异常,确保线程正常结束。
- 合理配置线程池,避免核心线程长期空闲。
- 必要时使用守护线程(
setDaemon(true),进程结束时守护线程自动终止)。
5. 线程池滥用
- 定义:不合理配置线程池参数、使用
Executors 静态方法、拒绝策略不当,导致系统性能下降或 OOM。
- 解决方案:
- 手动创建
ThreadPoolExecutor,合理配置七大参数。
- 选择合适的拒绝策略(如
CallerRunsPolicy,避免直接抛异常)。
- 监控线程池状态(如活跃线程数、队列大小),动态调整参数。
6. 阻塞与性能瓶颈
- 定义:线程阻塞(如等待锁、等待 IO)导致并发吞吐量下降,系统性能瓶颈。
- 解决方案:
- 优先选择无锁方案(
CAS/Atomic/LongAdder),减少线程阻塞。
- 缩小锁粒度(如
ConcurrentHashMap 的分段锁)。
- 减少锁持有时间,耗时操作(IO、数据库操作)移出锁外。
- 异步化处理(使用
CompletableFuture、消息队列),提高并发吞吐量。
五、真实高并发场景下的并发系统设计
设计一个既安全又高性能的高并发系统,需要结合「业务拆分」「并发工具」「性能优化」「容错机制」,形成一套完整的解决方案,核心思路如下:
1. 设计原则
- 「优先无锁,其次轻锁,最后重锁」:最小化锁开销,提升并发性能。
- 「最小化共享,最大化隔离」:减少共享资源,降低线程竞争。
- 「异步化,解耦化」:提高系统吞吐量,避免线程阻塞。
- 「监控调优,容错降级」:保证系统稳定性,应对极端场景。
2. 落地步骤
步骤 1:业务拆分与资源隔离
- 横向拆分:将大系统拆分为多个微服务(如订单服务、支付服务),每个服务独立部署,避免单机并发瓶颈。
- 纵向拆分:将一个服务内的功能拆分为多个模块,模块间尽量无共享资源,使用线程池隔离(不同模块使用不同线程池,避免一个模块故障影响其他模块)。
- 数据隔离:对共享数据进行分片(如数据库分库分表、Redis 分片),减少单节点的数据竞争。
步骤 2:选择合适的并发工具
- 计数场景(如接口访问量):使用
LongAdder(超高并发,分段累加,性能最优)。
- 简单变量修改(如订单状态):使用
Atomic 系列(AtomicInteger/AtomicReference)。
- 复杂业务逻辑(如订单创建):使用
ReentrantLock(支持超时、中断,避免死锁)。
- 缓存/映射场景(如用户信息缓存):使用
ConcurrentHashMap(线程安全,高并发性能优异)。
- 任务调度与管理:使用自定义
ThreadPoolExecutor(统一管理线程,避免资源耗尽)。
- 线程隔离场景(如日期格式化):使用
ThreadLocal(每个线程独立副本,无竞争)。
步骤 3:优化共享资源与并发性能
- 不可变对象:使用
final 修饰类和成员变量,创建不可变对象(如 String),避免修改共享资源,天然线程安全。
- 异步化处理:使用
CompletableFuture 实现异步调用(如并行查询多个数据库),使用消息队列(RocketMQ/Kafka)实现异步解耦(如订单创建后异步发送通知),提高系统吞吐量。
- 限流与削峰:使用 Sentinel/Guava RateLimiter 实现限流,避免系统被高并发压垮;使用消息队列削峰填谷,缓冲突发流量。
步骤 4:容错与监控
- 容错机制:实现服务降级(非核心业务故障时,关闭该业务,保证核心业务正常运行)、熔断(某个服务调用失败率过高时,暂停调用,避免雪崩)、重试(临时故障时,自动重试,提高成功率)。
- 监控调优:监控线程池状态(活跃线程数、队列大小、拒绝任务数)、锁竞争情况、CPU/内存占用、响应时间,根据监控数据动态调整参数(如线程池核心线程数、限流阈值)。
3. 示例场景:高并发订单计数系统
- 计数组件:使用
LongAdder 实现订单数累加(超高并发,无锁,性能最优)。
- 线程管理:使用自定义
ThreadPoolExecutor 处理订单请求,核心线程数配置为 CPU 核心数2,最大线程数配置为 CPU 核心数5,任务队列使用有界队列,拒绝策略使用 CallerRunsPolicy。
- 缓存优化:使用
ConcurrentHashMap 缓存订单信息,减少数据库查询。
- 异步化:订单创建后,使用
CompletableFuture 异步发送短信通知,不阻塞主线程。
- 限流:使用 Sentinel 限制订单请求 QPS 为 1000,避免系统过载。
- 监控:监控
LongAdder 的计数变化、线程池的活跃线程数、拒绝任务数,及时发现问题并调优。
总结
- 线程是进程内的轻量级执行单元,共享进程资源,线程通信成本低但健壮性差,进程相互隔离但创建销毁开销大。
- Java 线程创建优先选择
Runnable/Callable,企业级开发必须使用 ThreadPoolExecutor 手动创建线程池,避免资源耗尽。
- 线程安全的核心是解决原子性、可见性、有序性,选型优先
volatile(状态标记)→CAS/Atomic(高并发简单变量)→synchronized(低中并发简单场景)→ReentrantLock(复杂场景)。
- 常见并发问题(死锁、竞态条件等)的核心解决思路是「减少共享、统一顺序、避免阻塞、监控调优」。
- 高并发系统设计的核心是「业务拆分、资源隔离、异步化、容错限流」,平衡安全与性能,优先使用无锁和轻量级方案。