并发编程基础
并发与并行的区别
并发(Concurrency)指多个任务在单核CPU上通过时间片轮转交替执行,宏观上看似同时运行,微观实为串行。并行(Parallelism)则是真正的多任务同时执行,需多核CPU或分布式系统支持。例如,单核CPU的多线程是并发,多核CPU的多线程可实现真正并行。
线程与进程的概念
进程是操作系统资源分配的基本单位,拥有独立的地址空间和系统资源。线程是CPU调度的基本单位,同进程内的多线程共享进程资源(内存、文件描述符等),但各自维护程序计数器、栈和局部变量。
Java线程的生命周期及状态转换
Java线程包含六种状态:
- NEW:新建状态,线程已创建未启动
- RUNNABLE:可运行状态(包含就绪READY和运行中RUNNING)
- BLOCKED:阻塞状态,等待获取监视器锁
- WAITING:等待状态,需其他线程显式唤醒
- TIMED_WAITING:超时等待状态,定时自动唤醒
- TERMINATED:终止状态,线程执行完毕
状态转换路径:
- NEW → RUNNABLE:调用start()
- RUNNABLE → BLOCKED:等待锁
- RUNNABLE → WAITING:调用wait()/join()/park()
- RUNNABLE → TIMED_WAITING:调用sleep(timeout)等
- 阻塞/等待状态 → RUNNABLE:获取锁/被唤醒/超时
线程创建方式
继承Thread类
java
class MyThread extends Thread {
@Override
public void run() {
// 线程逻辑
}
}
MyThread t = new MyThread();
t.start();
实现Runnable接口
java
class MyRunnable implements Runnable {
@Override
public void run() {
// 线程逻辑
}
}
Thread t = new Thread(new MyRunnable());
t.start();
Callable与Future
java
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
// 线程逻辑
return "result";
}
}
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new MyCallable());
String result = future.get(); // 阻塞获取结果
线程安全与同步机制详解
线程安全问题根源
原子性问题详解
原子性问题指的是一个操作不可分割的特性被破坏。例如i++操作实际上包含三个步骤:
- 读取变量i的值到工作内存
- 在工作内存中进行加1运算
- 将结果写回主内存
在多线程环境下,如果两个线程同时执行i++操作,可能会出现如下时序问题:
线程A:读取i=0 → 计算i=1 → 写回i=1
线程B:读取i=0 → 计算i=1 → 写回i=1
最终结果i=1而不是预期的2,这就是典型的原子性问题。
可见性问题详解
可见性问题源于JMM的内存模型设计。每个线程有自己的工作内存,修改共享变量后不会立即同步到主内存。例如:
java
// 线程A
sharedFlag = true;
// 线程B
while(!sharedFlag) {
// 可能陷入死循环,即使线程A已经修改了sharedFlag
}
有序性问题详解
处理器和编译器会对指令进行重排序优化。例如:
java
// 初始状态
int a = 0;
boolean flag = false;
// 线程A
a = 1; // 语句1
flag = true; // 语句2
// 线程B
if(flag) { // 语句3
int i = a; // 语句4
}
由于指令重排序,线程A可能先执行语句2再执行语句1,导致线程B读取到a=0。
synchronized关键字深入
实现原理详解
-
Monitor对象:每个Java对象都与一个Monitor关联,包含:
- _owner:持有锁的线程
- _EntryList:等待获取锁的线程队列
- _WaitSet:调用wait()的线程集合
-
同步方法:编译后在方法访问标志添加ACC_SYNCHRONIZED,方法调用时自动获取对象的Monitor
-
同步代码块:
- monitorenter:尝试获取Monitor所有权
- monitorexit:释放Monitor所有权
- 编译器自动添加异常处理确保锁释放
锁升级过程(JDK6+)
- 无锁状态
- 偏向锁:记录线程ID,减少CAS开销
- 轻量级锁:通过CAS竞争锁
- 重量级锁:真正的Monitor竞争
volatile关键字详解
底层实现
- 写操作:加入StoreStore屏障 + StoreLoad屏障
- 读操作:加入LoadLoad屏障 + LoadStore屏障
典型应用场景
- 状态标志位:
java
volatile boolean shutdownRequested;
public void shutdown() { shutdownRequested = true; }
public void doWork() {
while(!shutdownRequested) {
// 执行任务
}
}
- 单例模式双重检查锁定:
java
class Singleton {
private volatile static Singleton instance;
public static Singleton getInstance() {
if(instance == null) {
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
锁分类详解
悲观锁实现对比
| 特性 | synchronized | ReentrantLock |
|---|---|---|
| 实现机制 | JVM内置 | JDK实现 |
| 可中断 | 不可 | 可 |
| 公平锁 | 非公平 | 可选 |
| 条件变量 | 单一 | 多条件 |
| 锁降级 | 不支持 | 不支持 |
乐观锁实现方式
- CAS原语:
java
AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.incrementAndGet();
- 版本号机制:
sql
UPDATE table SET value=newValue, version=version+1
WHERE id=#{id} AND version=#{oldVersion}
Java并发工具类详解
ReentrantLock高级特性
- 可中断锁获取:
java
lock.lockInterruptibly();
try {
// 临界区
} finally {
lock.unlock();
}
- 尝试获取锁:
java
if(lock.tryLock(1, TimeUnit.SECONDS)) {
try {
// 临界区
} finally {
lock.unlock();
}
} else {
// 超时处理
}
- 公平锁与非公平锁:
java
// 公平锁(按申请顺序获取)
ReentrantLock fairLock = new ReentrantLock(true);
// 非公平锁(可能插队)
ReentrantLock unfairLock = new ReentrantLock(false);
读写锁应用场景
适合读多写少场景,如:
- 缓存系统
- 配置信息管理
- 数据看板
典型使用模式:
java
class CachedData {
final ReadWriteLock rwl = new ReentrantReadWriteLock();
Object data;
boolean cacheValid;
void processCachedData() {
rwl.readLock().lock();
if(!cacheValid) {
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
if(!cacheValid) {
data = fetchDataFromDB();
cacheValid = true;
}
rwl.readLock().lock(); // 锁降级
} finally {
rwl.writeLock().unlock();
}
}
try {
use(data);
} finally {
rwl.readLock().unlock();
}
}
}
条件变量典型应用
- 生产者-消费者模型:
java
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while(count == items.length)
notFull.await();
items[putptr] = x;
if(++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while(count == 0)
notEmpty.await();
Object x = items[takeptr];
if(++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
- 线程交替执行:
java
class AlternatingThreads {
Lock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
boolean turnA = true;
void threadA() {
lock.lock();
try {
while(!turnA) {
conditionA.await();
}
// 执行A的工作
turnA = false;
conditionB.signal();
} finally {
lock.unlock();
}
}
void threadB() {
lock.lock();
try {
while(turnA) {
conditionB.await();
}
// 执行B的工作
turnA = true;
conditionA.signal();
} finally {
lock.unlock();
}
}
}
并发容器详解
ConcurrentHashMap演进
-
JDK7:分段锁机制
- 将数据分为16个段(Segment)
- 每个段独立加锁
- 并发度=分段数
-
JDK8+:
- 改为CAS+synchronized实现
- 数组+链表+红黑树结构
- 锁粒度缩小到单个链表/树节点
CopyOnWriteArrayList适用场景
- 监听器列表管理
- 只读或极少修改的场景
- 迭代频繁且不需要实时数据的场景
示例:
java
class EventManager {
private final CopyOnWriteArrayList<EventListener> listeners = new CopyOnWriteArrayList<>();
public void addListener(EventListener listener) {
listeners.add(listener);
}
public void removeListener(EventListener listener) {
listeners.remove(listener);
}
public void fireEvent(Event event) {
for(EventListener listener : listeners) {
listener.onEvent(event);
}
}
}
阻塞队列对比
| 队列类型 | 特性 | 适用场景 |
|---|---|---|
| ArrayBlockingQueue | 有界、数组实现、公平可选 | 固定资源池 |
| LinkedBlockingQueue | 可选有界、链表实现 | 无界任务队列 |
| PriorityBlockingQueue | 无界、优先级排序 | 任务优先级调度 |
| SynchronousQueue | 无缓冲、直接传递 | 手递手任务传递 |
| DelayQueue | 无界、延迟元素 | 定时任务调度 |
线程池深度解析
核心参数配置建议
-
corePoolSize:
- CPU密集型:N_cpu + 1
- IO密集型:N_cpu * 2
-
maximumPoolSize:
- 根据系统负载和资源限制设置
- 一般不超过corePoolSize的2-3倍
-
keepAliveTime:
- 突发流量场景:设置较短时间(30-60秒)
- 稳定流量场景:设置较长时间(5-10分钟)
-
工作队列选择:
- 快速响应:SynchronousQueue
- 平滑突发:LinkedBlockingQueue
- 限流保护:ArrayBlockingQueue
拒绝策略对比
- AbortPolicy(默认):抛出RejectedExecutionException
- CallerRunsPolicy:由调用线程执行任务
- DiscardPolicy:静默丢弃任务
- DiscardOldestPolicy:丢弃队列最老任务并重试
自定义拒绝策略示例:
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100),
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 记录日志
logger.warn("Task rejected: " + r.toString());
// 持久化任务
saveToDisk(r);
// 重试机制
if(!executor.isShutdown()) {
executor.submit(r);
}
}
});
线程池监控指标
-
核心指标:
- 活动线程数:getActiveCount()
- 任务队列大小:getQueue().size()
- 已完成任务数:getCompletedTaskCount()
-
扩展监控示例:
java
class MonitorThread implements Runnable {
private final ThreadPoolExecutor executor;
public MonitorThread(ThreadPoolExecutor executor) {
this.executor = executor;
}
@Override
public void run() {
while(true) {
System.out.println(
String.format("[monitor] [%d/%d] Active: %d, Completed: %d, Task: %d, isShutdown: %s, isTerminated: %s",
executor.getPoolSize(),
executor.getCorePoolSize(),
executor.getActiveCount(),
executor.getCompletedTaskCount(),
executor.getTaskCount(),
executor.isShutdown(),
executor.isTerminated()));
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
break;
}
}
}
}
CompletableFuture高级用法
组合操作
- thenCombine:合并两个独立Future的结果
java
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
future1.thenCombine(future2, (s1, s2) -> s1 + " " + s2)
.thenAccept(System.out::println);
- allOf/anyOf:
java
CompletableFuture<Void> all = CompletableFuture.allOf(future1, future2);
all.thenRun(() -> {
// 所有future完成
});
CompletableFuture<Object> any = CompletableFuture.anyOf(future1, future2);
any.thenAccept(result -> {
// 任意一个future完成
});
异常处理
- 多级异常处理:
java
CompletableFuture.supplyAsync(() -> {
// 可能抛出异常的操作
return doSomething();
})
.thenApply(result -> {
// 转换结果
return process(result);
})
.exceptionally(ex -> {
// 第一级异常处理
logger.error("Stage1 error", ex);
return defaultValue;
})
.thenAccept(result -> {
// 消费结果
consume(result);
})
.exceptionally(ex -> {
// 第二级异常处理
logger.error("Stage2 error", ex);
return null;
});
- handle方法统一处理:
java
CompletableFuture.supplyAsync(() -> riskyOperation())
.handle((result, ex) -> {
if(ex != null) {
// 异常处理
return fallbackValue;
}
// 正常处理
return result;
});
Java内存模型(JMM)详解
内存屏障类型
-
LoadLoad屏障:
- 确保Load1数据的装载先于Load2及后续装载指令
- 典型应用:volatile读操作后插入
-
StoreStore屏障:
- 确保Store1数据对其他处理器可见先于Store2及后续存储指令
- 典型应用:volatile写操作前插入
-
LoadStore屏障:
- 确保Load1数据装载先于Store2及后续存储指令
- 典型应用:volatile读操作后插入
-
StoreLoad屏障:
- 确保Store1数据对其他处理器可见先于Load2及后续装载指令
- 开销最大的屏障,对应volatile写操作后插入
happens-before规则应用
- 程序顺序规则:
java
int a = 1; // 操作A
int b = 2; // 操作B
// 操作A happens-before 操作B
- 监视器锁规则:
java
// 线程A
synchronized(lock) {
x = 1; // 操作A
}
// 线程B
synchronized(lock) {
int r = x; // 操作B
}
// 操作A happens-before 操作B
- volatile变量规则:
java
// 线程A
volatileFlag = true; // 操作A
// 线程B
if(volatileFlag) { // 操作B
// 操作A happens-before 操作B
}
- 线程启动规则:
java
Thread t = new Thread(() -> {
// 操作B
doSomething();
});
// 操作A
t.start();
// 操作A happens-before 操作B
- 线程终止规则:
java
Thread t = new Thread(() -> {
// 操作A
doSomething();
});
t.start();
t.join();
// 操作A happens-before 操作B
// 操作B
afterJoin();
final字段特殊规则
final字段的正确初始化保证:
java
class FinalFieldExample {
final int x;
int y;
static FinalFieldExample f;
public FinalFieldExample() {
x = 3;
y = 4;
}
static void writer() {
f = new FinalFieldExample();
}
static void reader() {
if(f != null) {
int i = f.x; // 保证看到3
int j = f.y; // 可能看到0
}
}
}