1. Java多线程编程核心概念解析
多线程作为Java语言最强大的特性之一,让程序能够同时执行多个任务。在操作系统层面,每个Java线程对应一个轻量级的进程,共享同一进程的内存空间。现代CPU普遍采用多核架构,合理使用多线程可以显著提升计算密集型任务的执行效率。
线程的生命周期包含新建(NEW)、就绪(RUNNABLE)、运行(RUNNING)、阻塞(BLOCKED)和终止(TERMINATED)五种状态。通过Thread类的getState()方法可以获取当前线程状态。值得注意的是,从RUNNABLE到RUNNING的转换由操作系统线程调度器控制,开发者无法直接干预。
关键提示:在Java中创建线程有继承Thread类和实现Runnable接口两种基本方式。实际开发中更推荐实现Runnable接口,因为Java不支持多重继承,这种方式更灵活且符合面向对象设计原则。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 线程同步与锁机制深度剖析
2.1 synchronized关键字原理
synchronized是Java最基础的同步机制,可以修饰方法或代码块。当修饰实例方法时,锁定的是当前对象实例(this);修饰静态方法时,锁定的是当前类的Class对象。JVM会为每个对象维护一个Monitor(监视器锁),synchronized本质上是通过获取和释放这个Monitor来实现同步的。
java复制public class Counter {
private int count;
// 同步实例方法
public synchronized void increment() {
count++;
}
// 同步代码块
public void decrement() {
synchronized(this) {
count--;
}
}
}
2.2 volatile与内存可见性
volatile关键字确保变量的修改对所有线程立即可见。它通过禁止指令重排序和强制刷新处理器缓存来实现可见性保证。但需要注意volatile不能保证原子性,适合一写多读的场景。
java复制public class VolatileExample {
private volatile boolean flag = false;
public void writer() {
flag = true; // 写操作
}
public void reader() {
if (flag) { // 读操作
// do something
}
}
}
2.3 JUC包中的高级锁
Java 5引入了java.util.concurrent包,提供了更强大的并发工具:
- ReentrantLock:可重入锁,支持公平/非公平策略
- ReadWriteLock:读写分离锁,提高读多写少场景性能
- StampedLock:乐观读锁,进一步优化读性能
java复制// ReentrantLock使用示例
Lock lock = new ReentrantLock();
try {
lock.lock();
// 临界区代码
} finally {
lock.unlock();
}
3. 线程池最佳实践
3.1 线程池核心参数
ThreadPoolExecutor的构造参数决定了线程池的行为:
- corePoolSize:核心线程数,即使空闲也不会被回收
- maximumPoolSize:最大线程数
- keepAliveTime:非核心线程空闲存活时间
- workQueue:任务队列
- threadFactory:线程工厂
- handler:拒绝策略
java复制ExecutorService executor = new ThreadPoolExecutor(
4, // 核心线程数
8, // 最大线程数
60, // 空闲时间
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100), // 任务队列
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
3.2 四种预定义线程池
Executors工厂类提供了四种常用线程池:
- newFixedThreadPool:固定大小线程池
- newCachedThreadPool:可缓存线程池
- newSingleThreadExecutor:单线程池
- newScheduledThreadPool:定时任务线程池
生产环境警告:阿里巴巴Java开发手册明确禁止使用Executors创建线程池,推荐通过ThreadPoolExecutor构造函数明确指定参数,避免资源耗尽风险。
4. 并发集合类使用指南
4.1 ConcurrentHashMap实现原理
ConcurrentHashMap采用分段锁技术(JDK7)或CAS+synchronized(JDK8+),在保证线程安全的同时提供接近HashMap的性能。关键特性包括:
- 默认16个并发段(JDK7)
- 负载因子0.75
- 树化阈值8(链表转红黑树)
java复制ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
map.computeIfAbsent("key", k -> 1); // 原子操作
4.2 CopyOnWrite容器
CopyOnWriteArrayList和CopyOnWriteArraySet采用写时复制策略,适合读多写少的场景。每次修改操作都会创建底层数组的新副本,读操作不需要同步。
java复制List<String> list = new CopyOnWriteArrayList<>();
list.add("item"); // 创建新数组
String item = list.get(0); // 无需同步
5. 线程间通信机制
5.1 wait/notify机制
Object类提供的wait()、notify()和notifyAll()方法实现线程间协作。使用时必须持有对象监视器锁(在synchronized块中调用)。
java复制public class WaitNotifyExample {
private final Object lock = new Object();
private boolean condition = false;
public void await() throws InterruptedException {
synchronized(lock) {
while(!condition) {
lock.wait();
}
// 条件满足后执行
}
}
public void signal() {
synchronized(lock) {
condition = true;
lock.notifyAll();
}
}
}
5.2 Condition接口
Lock接口的newCondition()方法可以创建更灵活的等待/通知机制,支持多个等待队列和选择性通知。
java复制Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try {
condition.await(); // 相当于wait()
condition.signal(); // 相当于notify()
} finally {
lock.unlock();
}
6. 常见并发问题与解决方案
6.1 死锁检测与预防
死锁的四个必要条件:
- 互斥条件
- 请求与保持
- 不剥夺条件
- 循环等待
避免死锁的策略:
- 按固定顺序获取锁
- 使用tryLock()设置超时
- 通过jstack或VisualVM检测死锁
java复制// 按顺序获取锁避免死锁
public void transfer(Account from, Account to, int amount) {
Account first = from.hashCode() < to.hashCode() ? from : to;
Account second = from.hashCode() < to.hashCode() ? to : from;
synchronized(first) {
synchronized(second) {
// 转账操作
}
}
}
6.2 线程安全设计模式
- 不可变对象模式:使用final字段和防御性拷贝
- 线程封闭模式:栈封闭或ThreadLocal
- 生产者-消费者模式:BlockingQueue实现
- Fork-Join模式:分治算法并行化
java复制// ThreadLocal使用示例
public class UserContext {
private static final ThreadLocal<User> currentUser = new ThreadLocal<>();
public static void set(User user) {
currentUser.set(user);
}
public static User get() {
return currentUser.get();
}
public static void remove() {
currentUser.remove();
}
}
7. Java内存模型与happens-before规则
Java内存模型(JMM)定义了线程如何以及何时可以看到其他线程写入共享变量的值。关键的happens-before规则包括:
- 程序顺序规则
- 监视器锁规则
- volatile变量规则
- 线程启动规则
- 线程终止规则
- 中断规则
- 终结器规则
- 传递性
理解这些规则对于编写正确的并发程序至关重要。例如,volatile变量的写操作happens-before后续对该变量的读操作,这保证了可见性。
8. 性能调优实战技巧
8.1 减少锁竞争
- 缩小同步范围:只同步必要的代码块
- 降低锁粒度:如ConcurrentHashMap的分段锁
- 使用读写锁:ReadWriteLock
- 无锁算法:CAS操作
java复制// 缩小同步范围示例
public void process() {
// 非同步代码
synchronized(this) {
// 必须同步的代码
}
// 更多非同步代码
}
8.2 上下文切换优化
- 避免过度创建线程
- 使用合适的线程池大小
- 考虑协程(Project Loom的虚拟线程)
- 减少锁持有时间
经验法则:对于CPU密集型任务,线程数≈CPU核心数;对于IO密集型任务,可以适当增加线程数。可以通过Runtime.getRuntime().availableProcessors()获取CPU核心数。
9. Java并发工具类实战
9.1 CountDownLatch
允许一个或多个线程等待其他线程完成操作。初始化时指定计数器,countDown()递减计数器,await()阻塞直到计数器为零。
java复制// 等待多个任务完成
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 执行任务
latch.countDown();
}).start();
}
latch.await(); // 等待所有任务完成
9.2 CyclicBarrier
让一组线程到达屏障时被阻塞,直到最后一个线程到达屏障才会继续执行。与CountDownLatch不同,它可以重置重用。
java复制// 多阶段任务同步
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
// 所有线程到达后执行
});
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 阶段1
barrier.await();
// 阶段2
}).start();
}
10. Java虚拟线程前瞻
Java 19引入了虚拟线程(预览特性),这是由JVM管理的轻量级线程,可以显著提高高并发应用的吞吐量。与传统操作系统线程相比:
- 创建成本极低
- 数量可达百万级
- 由JVM调度,不绑定OS线程
java复制// 虚拟线程使用示例(Java 19+)
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
return i;
});
});
}
虚拟线程特别适合处理大量并发IO操作,如微服务调用、数据库访问等场景。它们与传统线程API兼容,可以平滑迁移现有代码。
