第7篇:Java多线程与并发编程深度解析
在当今高并发的应用场景下,多线程与并发编程已成为Java开发者必须掌握的核心技能。无论是提升程序性能、优化资源利用率,还是应对复杂的业务逻辑,多线程技术都扮演着关键角色。本文将深入剖析Java多线程与并发编程的底层原理、核心工具类、实战技巧以及性能优化策略,全文超过万字,助你构建系统的并发知识体系。
一、多线程基础与核心概念
1. 进程与线程的本质区别
- 进程:操作系统资源分配的最小单位,每个进程拥有独立的内存空间和系统资源。
- 线程:CPU调度的最小单位,共享进程的内存空间,切换开销远低于进程。
示例代码:查看Java进程线程数
public class ProcessThreadDemo {
public static void main(String[] args) {
System.out.println("当前进程ID:" + ProcessHandle.current().pid());
System.out.println("活跃线程数:" + Thread.activeCount());
}
}
2. Java线程的六种状态(源码级解析)
public enum State {
NEW, // 尚未启动
RUNNABLE, // JVM中执行(包含操作系统就绪态)
BLOCKED, // 等待监视器锁
WAITING, // 无限期等待(Object.wait()/join())
TIMED_WAITING,// 限期等待(sleep(ms)/wait(ms))
TERMINATED; // 线程终止
}
3. 线程优先级与调度机制
- 优先级范围:1(MIN_PRIORITY)到10(MAX_PRIORITY)
- 调度策略:Java依赖操作系统调度(优先级仅作为参考)
二、线程创建与生命周期管理(附源码分析)
1. 三种创建方式对比
- 继承Thread类:简单但扩展性差
- 实现Runnable接口:推荐方式,解耦任务与线程
- 实现Callable接口:支持返回值与异常抛出
示例代码:Callable + FutureTask
Callable<Integer> task = () -> {
TimeUnit.SECONDS.sleep(2);
return 42;
};
FutureTask<Integer> futureTask = new FutureTask<>(task);
new Thread(futureTask).start();
System.out.println("计算结果:" + futureTask.get());
2. 线程中断机制(Interruption)
- 核心方法:
interrupt():设置中断标志isInterrupted():检查中断状态Thread.interrupted():静态方法,清除中断状态
正确的中断处理模式
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
// 阻塞操作需处理InterruptedException
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 重新设置中断标志
break;
}
}
}
三、线程同步与锁机制(深入JVM层)
1. synchronized的底层实现
- 对象头结构:Mark Word中的锁标志位
- 锁升级过程:无锁 → 偏向锁 → 轻量级锁 → 重量级锁
偏向锁优化案例
public class BiasLockDemo {
public static void main(String[] args) {
Object lock = new Object();
// 偏向锁延迟默认启用(JVM启动后4秒)
try { TimeUnit.SECONDS.sleep(5); } catch (Exception e) {}
synchronized (lock) { // 此时触发偏向锁
System.out.println("第一次加锁");
}
synchronized (lock) { // 偏向锁重入
System.out.println("第二次加锁");
}
}
}
2. Lock体系深度解析
- ReentrantLock:可中断锁、公平锁实现
- ReentrantReadWriteLock:读写分离锁优化
- StampedLock:乐观读锁(Java 8+)
StampedLock性能优化示例
public class StampedLockDemo {
private final StampedLock sl = new StampedLock();
private double x, y;
void move(double deltaX, double deltaY) {
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
double distanceFromOrigin() {
long stamp = sl.tryOptimisticRead();
double currentX = x, currentY = y;
if (!sl.validate(stamp)) {
stamp = sl.readLock();
try {
currentX = x;
currentY = y;
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
}
四、Java内存模型(JMM)与happens-before原则
1. JMM核心概念
- 主内存与工作内存
- 内存间交互操作(read/load/use/assign/store/write)
2. happens-before八大规则
- 程序顺序规则
- 锁规则(解锁先于加锁)
- volatile变量规则
- 传递性规则
volatile的底层实现
public class VolatileExample {
private volatile boolean flag = false;
public void writer() {
flag = true; // StoreStore屏障
}
public void reader() {
if (flag) { // LoadLoad屏障
// 读取操作
}
}
}
五、并发工具类深度解析
1. AQS(AbstractQueuedSynchronizer)原理
- CLH队列实现
- 模板方法设计模式
自定义同步器示例
public class Mutex extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
return compareAndSetState(0, 1);
}
@Override
protected boolean tryRelease(int arg) {
setState(0);
return true;
}
public static void main(String[] args) {
final Mutex mutex = new Mutex();
new Thread(() -> {
mutex.acquire(1);
try {
System.out.println("Thread1获取锁");
TimeUnit.SECONDS.sleep(2);
} finally {
mutex.release(1);
}
}).start();
new Thread(() -> {
mutex.acquire(1);
try {
System.out.println("Thread2获取锁");
} finally {
mutex.release(1);
}
}).start();
}
}
2. 并发容器原理
- ConcurrentHashMap:分段锁(Java 7) vs CAS+synchronized(Java 8+)
- CopyOnWriteArrayList:写时复制机制
Java 8 ConcurrentHashMap源码片段解析
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))
break; // CAS成功插入
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
// ...省略链表/红黑树处理
}
}
addCount(1L, binCount);
return null;
}
六、线程池原理与最佳实践
1. Executor框架体系
- ThreadPoolExecutor七大核心参数
- corePoolSize
- maximumPoolSize
- keepAliveTime
- unit
- workQueue
- threadFactory
- handler(拒绝策略)
自定义拒绝策略示例
public class CustomRejectionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.err.println("任务被拒绝:" + r.toString());
if (!executor.isShutdown()) {
r.run(); // 由主线程执行
}
}
}
2. 线程池监控与调优
- 监控指标:活跃线程数、队列大小、完成任务数
- 动态调整参数(需要自定义线程池)
public class DynamicThreadPool extends ThreadPoolExecutor {
public DynamicThreadPool(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
public void setCorePoolSize(int corePoolSize) {
super.setCorePoolSize(corePoolSize);
}
public void setMaximumPoolSize(int maximumPoolSize) {
super.setMaximumPoolSize(maximumPoolSize);
}
}
七、Java并发编程的演进:从Fork/Join到虚拟线程
1. Fork/Join框架原理
- 工作窃取算法(Work-Stealing)
- 递归任务拆分示例
public class FibonacciTask extends RecursiveTask<Integer> {
final int n;
FibonacciTask(int n) { this.n = n; }
protected Integer compute() {
if (n <= 1) return n;
FibonacciTask f1 = new FibonacciTask(n - 1);
f1.fork();
FibonacciTask f2 = new FibonacciTask(n - 2);
return f2.compute() + f1.join();
}
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
System.out.println(pool.invoke(new FibonacciTask(10)));
}
}
2. 虚拟线程(Loom项目,Java 19+)
- 轻量级线程:百万级线程支持
- 结构化并发(Java 21正式特性)
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<String> user = scope.fork(() -> fetchUser());
Future<Integer> order = scope.fork(() -> fetchOrder());
scope.join(); // 等待所有任务
scope.throwIfFailed(); // 异常传播
System.out.println("结果: " + user.resultNow() + order.resultNow());
}
八、并发调试与性能优化实战
1. 线程转储分析
- jstack命令:
jstack -l <pid> > thread_dump.txt - 识别死锁:搜索"deadlock"关键词
2. 并发问题检测工具
- Arthas:动态监控线程状态
- JProfiler:锁竞争分析
- JCStress:并发压力测试工具
Arthas监控线程示例
# 启动arthas
java -jar arthas-boot.jar
# 监控线程状态
thread -n 5
# 查看死锁
thread -b
九、常见并发问题与解决方案
| 问题类型 | 典型表现 | 解决方案 |
|---|---|---|
| 死锁 | 线程永久阻塞 | 顺序加锁、设置超时 |
| 活锁 | 线程持续重试失败 | 引入随机退避机制 |
| 资源竞争 | CPU利用率高但吞吐低 | 减少锁粒度、使用无锁数据结构 |
| 内存泄漏 | OOM异常 | 检查线程局部变量使用 |
| 上下文切换过多 | 系统负载高但CPU利用率低 | 减少线程数、使用异步IO |
十、总结与最佳实践
-
并发设计原则:
- 优先使用并发工具类而非直接操作线程
- 最小化同步范围(锁粒度控制)
- 使用不可变对象和线程安全容器
-
性能优化路径:
-
未来趋势:
- 响应式编程(Reactive Streams)
- 协程与虚拟线程的普及
- 硬件级并发支持(如向量指令)
下一篇预告:我们将深入探讨Java性能调优与JVM原理,涵盖GC算法、内存模型、JIT优化等核心内容。从理论到工具实战,助你打造高性能Java应用!
1004

被折叠的 条评论
为什么被折叠?



