Java多线程详解:从入门到精通(实战篇)
本文基于JDK 17,结合真实项目经验,深入浅出讲解Java多线程技术
目录
一、为什么需要多线程?
1.1 真实场景引入
在我之前负责的电商系统中,遇到过这样一个问题:
public OrderResult createOrder(OrderRequest request) {
// 1. 扣减库存 - 耗时 200ms
inventoryService.deduct(request.getProductId(), request.getQuantity());
// 2. 创建订单 - 耗时 150ms
Order order = orderService.create(request);
// 3. 发送短信通知 - 耗时 300ms
smsService.sendOrderNotification(order);
// 4. 记录日志 - 耗时 50ms
logService.recordOrder(order);
return new OrderResult(order);
}
问题:整个流程串行执行,总耗时 700ms,用户体验差。
优化后:使用多线程并行处理非核心流程
public OrderResult createOrder(OrderRequest request) {
// 核心流程:扣减库存 + 创建订单(同步执行)
inventoryService.deduct(request.getProductId(), request.getQuantity());
Order order = orderService.create(request);
// 非核心流程:异步执行
CompletableFuture.runAsync(() ->
smsService.sendOrderNotification(order), asyncExecutor);
CompletableFuture.runAsync(() ->
logService.recordOrder(order), asyncExecutor);
return new OrderResult(order);
}
效果:响应时间从 700ms 降低到 350ms,提升 50%!
1.2 多线程的核心价值
- 提升响应速度:并行处理多个任务
- 提高资源利用率:充分利用多核CPU
- 改善用户体验:避免界面卡顿
- 增强系统吞吐量:同时处理更多请求
二、线程基础:从零开始
2.1 创建线程的四种方式
方式一:继承Thread类(不推荐)
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
缺点:Java单继承限制,无法继承其他类。
方式二:实现Runnable接口(推荐)
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程执行:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
}
优点:解耦任务和线程,可以继承其他类。
方式三:实现Callable接口(有返回值)
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "任务执行完成:" + Thread.currentThread().getName();
}
public static void main(String[] args) throws Exception {
FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
new Thread(futureTask).start();
// 获取返回值(会阻塞)
String result = futureTask.get();
System.out.println(result);
}
}
优点:可以获取返回值,可以抛出异常。
方式四:线程池(生产环境首选)
public class ThreadPoolExample {
private static final ExecutorService executor =
Executors.newFixedThreadPool(10);
public static void main(String[] args) {
executor.submit(() -> {
System.out.println("线程池执行任务");
});
executor.shutdown();
}
}
优点:复用线程,避免频繁创建销毁,性能最优。
2.2 线程生命周期
NEW(新建)
↓ start()
RUNNABLE(就绪/运行)
↓ 获取锁失败 / wait() / sleep() / join() / LockSupport.park()
BLOCKED / WAITING / TIMED_WAITING(阻塞/等待)
↓ 获取锁 / notify() / 超时 / interrupt()
RUNNABLE(就绪/运行)
↓ run()执行完毕
TERMINATED(终止)
2.3 实战案例:批量数据处理
场景:需要处理100万条数据,单线程太慢。
public class BatchProcessor {
private static final int THREAD_COUNT = 8;
private static final int BATCH_SIZE = 10000;
public void processBigData(List<Data> dataList) throws InterruptedException {
int totalSize = dataList.size();
CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
for (int i = 0; i < THREAD_COUNT; i++) {
int start = i * (totalSize / THREAD_COUNT);
int end = (i == THREAD_COUNT - 1) ? totalSize : (i + 1) * (totalSize / THREAD_COUNT);
new Thread(() -> {
try {
List<Data> subList = dataList.subList(start, end);
for (Data data : subList) {
processData(data);
}
} finally {
latch.countDown();
}
}, "Worker-" + i).start();
}
latch.await();
System.out.println("所有数据处理完成");
}
private void processData(Data data) {
// 具体业务逻辑
}
}
性能对比:
- 单线程:100万条数据耗时 50秒
- 8线程并行:100万条数据耗时 7秒
- 性能提升 7倍!
三、线程安全问题与解决方案
3.1 经典问题:账户转账
不安全的代码:
public class BankAccount {
private int balance = 1000;
public void transfer(BankAccount target, int amount) {
if (this.balance >= amount) {
this.balance -= amount;
target.balance += amount;
}
}
}
问题:多线程并发转账时,可能出现余额不一致。
3.2 解决方案一:synchronized关键字
public class BankAccount {
private int balance = 1000;
public synchronized void transfer(BankAccount target, int amount) {
if (this.balance >= amount) {
this.balance -= amount;
target.balance += amount;
}
}
}
注意:synchronized锁的是对象,要避免死锁!
死锁示例:
// 线程1:A转账给B
accountA.transfer(accountB, 100);
// 线程2:B转账给A
accountB.transfer(accountA, 100);
解决死锁:按固定顺序加锁
public void transfer(BankAccount target, int amount) {
BankAccount first = this.hashCode() < target.hashCode() ? this : target;
BankAccount second = this.hashCode() < target.hashCode() ? target : this;
synchronized (first) {
synchronized (second) {
if (this.balance >= amount) {
this.balance -= amount;
target.balance += amount;
}
}
}
}
3.3 解决方案二:ReentrantLock
public class BankAccount {
private int balance = 1000;
private final ReentrantLock lock = new ReentrantLock();
public void transfer(BankAccount target, int amount) {
lock.lock();
try {
if (this.balance >= amount) {
this.balance -= amount;
target.balance += amount;
}
} finally {
lock.unlock();
}
}
}
ReentrantLock vs synchronized:
| 特性 | synchronized | ReentrantLock |
|---|---|---|
| 使用方式 | 关键字,自动释放 | API调用,手动释放 |
| 可中断 | 不支持 | 支持 tryLock() |
| 公平锁 | 非公平 | 可选公平/非公平 |
| 条件变量 | 单个wait/notify | 多个Condition |
| 性能 | JDK6后优化,相当 | 略优 |
实战建议:
- 简单场景用 synchronized
- 需要高级特性用 ReentrantLock
3.4 解决方案三:原子类
public class Counter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
public int getCount() {
return count.get();
}
}
常用原子类:
AtomicInteger、AtomicLong:基本类型AtomicReference:对象引用AtomicStampedReference:解决ABA问题LongAdder:高并发计数器(性能更优)
3.5 实战案例:高并发计数器
场景:统计网站访问量,每秒10万次请求。
public class VisitCounter {
// 错误方式:synchronized性能差
private int count = 0;
public synchronized void increment() {
count++;
}
// 正确方式:LongAdder性能优
private LongAdder counter = new LongAdder();
public void increment() {
counter.increment();
}
public long getCount() {
return counter.sum();
}
}
性能测试:
- synchronized:100万次递增耗时 800ms
- AtomicLong:100万次递增耗时 300ms
- LongAdder:100万次递增耗时 50ms
LongAdder为什么快?
- 内部使用分段锁思想
- 多个线程操作不同的Cell
- 最后汇总所有Cell的值
四、线程池:生产环境的最佳实践
4.1 为什么必须使用线程池?
反面教材:每次请求创建新线程
@RestController
public class UserController {
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
new Thread(() -> {
logService.recordAccess(id);
}).start();
return userService.getById(id);
}
}
问题:
- 创建线程开销大(1MB栈空间)
- 无法控制并发数,可能OOM
- 线程频繁创建销毁,CPU浪费
4.2 线程池核心参数详解
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
执行流程:
提交任务
↓
核心线程数未满?
↓ 是
创建核心线程执行
↓ 否
任务队列未满?
↓ 是
加入队列等待
↓ 否
最大线程数未满?
↓ 是
创建非核心线程执行
↓ 否
执行拒绝策略
4.3 实战配置:不同场景的线程池
场景一:CPU密集型任务(计算为主)
@Configuration
public class ThreadPoolConfig {
@Bean("cpuIntensiveExecutor")
public ThreadPoolExecutor cpuIntensiveExecutor() {
int coreCount = Runtime.getRuntime().availableProcessors();
return new ThreadPoolExecutor(
coreCount, // 核心线程数 = CPU核心数
coreCount, // 最大线程数 = CPU核心数
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000),
new ThreadFactoryBuilder()
.setNameFormat("cpu-pool-%d")
.build(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
}
}
适用场景:数据加密、图像处理、复杂计算
场景二:IO密集型任务(网络/磁盘IO)
@Bean("ioIntensiveExecutor")
public ThreadPoolExecutor ioIntensiveExecutor() {
int coreCount = Runtime.getRuntime().availableProcessors();
return new ThreadPoolExecutor(
coreCount * 2, // 核心线程数 = CPU核心数 * 2
coreCount * 4, // 最大线程数 = CPU核心数 * 4
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(2000),
new ThreadFactoryBuilder()
.setNameFormat("io-pool-%d")
.setUncaughtExceptionHandler((t, e) ->
log.error("线程异常:{}", t.getName(), e))
.build(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
}
适用场景:HTTP请求、数据库查询、文件读写
场景三:定时任务
@Bean("scheduledExecutor")
public ScheduledThreadPoolExecutor scheduledExecutor() {
return new ScheduledThreadPoolExecutor(
5,
new ThreadFactoryBuilder()
.setNameFormat("scheduled-pool-%d")
.build()
);
}
使用示例:
@Service
public class ScheduledService {
@Autowired
private ScheduledThreadPoolExecutor scheduledExecutor;
public void startScheduledTask() {
scheduledExecutor.scheduleAtFixedRate(
() -> cleanExpiredData(),
0, // 初始延迟
1, // 执行间隔
TimeUnit.HOURS // 时间单位
);
}
}
4.4 拒绝策略选择
| 策略 | 说明 | 适用场景 |
|---|---|---|
| AbortPolicy(默认) | 抛出异常 | 需要感知任务被拒绝 |
| CallerRunsPolicy | 调用者线程执行 | 重要任务不能丢失 |
| DiscardPolicy | 静默丢弃 | 允许丢失的任务 |
| DiscardOldestPolicy | 丢弃最旧任务 | 优先执行新任务 |
自定义拒绝策略:
public class CustomRejectedHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
log.warn("任务被拒绝,当前队列大小:{}", executor.getQueue().size());
try {
executor.getQueue().offer(r, 3, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.error("任务重新提交失败", e);
}
}
}
4.5 实战案例:异步日志系统
@Component
public class AsyncLogService {
private final ThreadPoolExecutor logExecutor;
public AsyncLogService() {
this.logExecutor = new ThreadPoolExecutor(
2,
4,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10000),
new ThreadFactoryBuilder()
.setNameFormat("async-log-%d")
.setDaemon(true)
.build(),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
}
public void logAsync(String message) {
logExecutor.execute(() -> {
try {
writeToFile(message);
} catch (Exception e) {
System.err.println("日志写入失败:" + e.getMessage());
}
});
}
private void writeToFile(String message) {
// 写入文件逻辑
}
@PreDestroy
public void shutdown() {
logExecutor.shutdown();
try {
if (!logExecutor.awaitTermination(10, TimeUnit.SECONDS)) {
logExecutor.shutdownNow();
}
} catch (InterruptedException e) {
logExecutor.shutdownNow();
}
}
}
4.6 线程池监控
@Component
@Slf4j
public class ThreadPoolMonitor {
@Scheduled(fixedRate = 60000)
public void monitorThreadPool() {
ThreadPoolExecutor executor = getExecutor();
log.info("线程池监控 - " +
"核心线程数:{}, " +
"活跃线程数:{}, " +
"最大线程数:{}, " +
"队列大小:{}, " +
"已完成任务数:{}, " +
"总任务数:{}",
executor.getCorePoolSize(),
executor.getActiveCount(),
executor.getMaximumPoolSize(),
executor.getQueue().size(),
executor.getCompletedTaskCount(),
executor.getTaskCount()
);
if (executor.getQueue().size() > 1000) {
log.warn("线程池队列积压严重,考虑扩容!");
}
}
}
五、并发工具类实战
5.1 CountDownLatch:等待多个线程完成
场景:系统启动时需要预加载多个资源
@Component
public class SystemInitializer {
public void initialize() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
new Thread(() -> {
try {
loadConfig();
} finally {
latch.countDown();
}
}).start();
new Thread(() -> {
try {
loadCache();
} finally {
latch.countDown();
}
}).start();
new Thread(() -> {
try {
connectDatabase();
} finally {
latch.countDown();
}
}).start();
latch.await(30, TimeUnit.SECONDS);
System.out.println("系统初始化完成");
}
}
5.2 CyclicBarrier:循环栅栏
场景:多线程分段计算,每个阶段需要同步
public class ParallelCalculator {
private final CyclicBarrier barrier;
private final int threadCount = 4;
private final double[] results = new double[threadCount];
public ParallelCalculator() {
this.barrier = new CyclicBarrier(threadCount, () -> {
double sum = Arrays.stream(results).sum();
System.out.println("本轮计算完成,总和:" + sum);
});
}
public void calculate() {
for (int i = 0; i < threadCount; i++) {
final int index = i;
new Thread(() -> {
for (int round = 0; round < 3; round++) {
results[index] = Math.random() * 100;
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
}
5.3 Semaphore:信号量限流
场景:限制同时访问数据库的连接数
@Component
public class DatabaseConnectionPool {
private final Semaphore semaphore = new Semaphore(10);
public void executeQuery(String sql) {
try {
semaphore.acquire();
try {
doQuery(sql);
} finally {
semaphore.release();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private void doQuery(String sql) {
// 执行数据库查询
}
}
5.4 CompletableFuture:异步编排神器
场景:商品详情页需要并行查询多个服务
@Service
public class ProductDetailService {
public ProductDetailVO getProductDetail(Long productId) {
CompletableFuture<Product> productFuture =
CompletableFuture.supplyAsync(() ->
productService.getById(productId));
CompletableFuture<List<Comment>> commentFuture =
CompletableFuture.supplyAsync(() ->
commentService.listByProductId(productId));
CompletableFuture<Inventory> inventoryFuture =
CompletableFuture.supplyAsync(() ->
inventoryService.getByProductId(productId));
CompletableFuture<Void> allFuture = CompletableFuture.allOf(
productFuture, commentFuture, inventoryFuture
);
allFuture.join();
return ProductDetailVO.builder()
.product(productFuture.join())
.comments(commentFuture.join())
.inventory(inventoryFuture.join())
.build();
}
}
高级用法:异常处理和超时控制
public CompletableFuture<String> callExternalApi() {
return CompletableFuture.supplyAsync(() -> {
return httpClient.get("https://api.example.com/data");
})
.orTimeout(3, TimeUnit.SECONDS)
.exceptionally(ex -> {
log.error("API调用失败", ex);
return "默认值";
});
}
六、常见并发问题排查
6.1 死锁排查
死锁代码示例:
public class DeadLockDemo {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock1) {
System.out.println("线程1获取lock1");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock2) {
System.out.println("线程1获取lock2");
}
}
}).start();
new Thread(() -> {
synchronized (lock2) {
System.out.println("线程2获取lock2");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock1) {
System.out.println("线程2获取lock1");
}
}
}).start();
}
}
排查方法:
- 使用jstack命令:
# 获取进程ID
jps
# 导出线程堆栈
jstack <pid> > thread_dump.txt
# 查找死锁信息
grep -A 20 "Found one Java-level deadlock" thread_dump.txt
-
使用JConsole可视化工具:
- 连接到Java进程
- 查看"线程"标签
- 点击"检测死锁"按钮
-
代码预防:
public boolean tryTransfer(BankAccount target, int amount, long timeout) {
long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < timeout) {
if (this.lock.tryLock()) {
try {
if (target.lock.tryLock()) {
try {
if (this.balance >= amount) {
this.balance -= amount;
target.balance += amount;
return true;
}
} finally {
target.lock.unlock();
}
}
} finally {
this.lock.unlock();
}
}
try { Thread.sleep(10); } catch (InterruptedException e) {}
}
return false;
}
6.2 内存泄漏排查
常见原因:ThreadLocal未清理
public class ThreadLocalLeakDemo {
private static ThreadLocal<byte[]> threadLocal = new ThreadLocal<>();
public void process() {
threadLocal.set(new byte[1024 * 1024]);
// 忘记清理,导致内存泄漏
}
}
正确做法:
public void process() {
try {
threadLocal.set(new byte[1024 * 1024]);
// 业务逻辑
} finally {
threadLocal.remove();
}
}
排查工具:
# 导出堆内存快照
jmap -dump:live,format=b,file=heap.hprof <pid>
# 使用MAT工具分析heap.hprof文件
# 查找Dominator Tree,找出占用内存最大的对象
6.3 CPU飙高排查
步骤:
- 找出占用CPU最高的线程:
# 查看进程CPU使用率
top -p <pid>
# 查看线程CPU使用率
top -Hp <pid>
# 记录占用CPU最高的线程ID(十进制)
# 假设线程ID为 12345
- 转换线程ID为十六进制:
printf "%x\n" 12345
# 输出:3039
- 导出线程堆栈并查找:
jstack <pid> | grep -A 50 "0x3039"
- 分析代码:
// 常见原因:死循环
while (true) {
// 没有sleep或wait,CPU空转
}
// 正确做法:
while (running) {
try {
doWork();
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
6.4 线程池队列积压
监控告警:
@Component
@Slf4j
public class ThreadPoolAlertMonitor {
@Scheduled(fixedRate = 10000)
public void checkThreadPool() {
ThreadPoolExecutor executor = getExecutor();
int queueSize = executor.getQueue().size();
int activeCount = executor.getActiveCount();
int poolSize = executor.getPoolSize();
if (queueSize > 1000) {
log.error("线程池队列积压严重!队列大小:{}, 活跃线程:{}, 线程池大小:{}",
queueSize, activeCount, poolSize);
sendAlert("线程池队列积压告警");
}
if (activeCount == poolSize && queueSize > 0) {
log.warn("线程池已满负荷运行,考虑扩容");
}
}
}
七、性能优化技巧
7.1 减少锁的粒度
优化前:
public class UserService {
private final Map<Long, User> userCache = new HashMap<>();
public synchronized User getUser(Long id) {
return userCache.get(id);
}
public synchronized void putUser(User user) {
userCache.put(user.getId(), user);
}
}
优化后:使用ConcurrentHashMap
public class UserService {
private final ConcurrentHashMap<Long, User> userCache = new ConcurrentHashMap<>();
public User getUser(Long id) {
return userCache.get(id);
}
public void putUser(User user) {
userCache.put(user.getId(), user);
}
}
性能提升:并发读写性能提升10倍以上!
7.2 使用局部变量避免共享
优化前:
public class Calculator {
private int result = 0;
public synchronized void calculate(int value) {
result += value;
result *= 2;
result -= 10;
}
}
优化后:
public class Calculator {
private AtomicInteger result = new AtomicInteger(0);
public void calculate(int value) {
int temp = value;
temp *= 2;
temp -= 10;
result.addAndGet(temp);
}
}
7.3 批量操作减少锁竞争
优化前:
for (int i = 0; i < 10000; i++) {
cache.put(i, "value" + i);
}
优化后:
Map<Integer, String> batch = new HashMap<>();
for (int i = 0; i < 10000; i++) {
batch.put(i, "value" + i);
}
cache.putAll(batch);
7.4 使用无锁数据结构
性能对比:
| 数据结构 | 并发性能 | 适用场景 |
|---|---|---|
| Vector | 差 | 不推荐 |
| Collections.synchronizedList | 差 | 不推荐 |
| CopyOnWriteArrayList | 读多写少 | 配置列表 |
| ConcurrentLinkedQueue | 优秀 | 任务队列 |
| LinkedBlockingQueue | 良好 | 生产者消费者 |
八、面试高频问题
8.1 synchronized和ReentrantLock的区别?
答案要点:
-
实现层面:
- synchronized是JVM层面的关键字
- ReentrantLock是JDK层面的API
-
功能对比:
- synchronized自动释放锁,ReentrantLock需要手动释放
- ReentrantLock支持tryLock()尝试获取锁
- ReentrantLock支持公平锁和非公平锁
- ReentrantLock支持多个Condition条件变量
-
性能:JDK6后synchronized优化,性能相当
-
使用建议:简单场景用synchronized,需要高级特性用ReentrantLock
8.2 volatile关键字的作用?
答案要点:
- 保证可见性:一个线程修改后,其他线程立即可见
- 禁止指令重排序:保证有序性
- 不保证原子性:i++操作不是原子的
代码示例:
public class VolatileDemo {
private volatile boolean flag = false;
public void writer() {
flag = true;
}
public void reader() {
if (flag) {
// 一定能看到flag的最新值
}
}
}
8.3 线程池参数如何设置?
答案要点:
- CPU密集型:核心线程数 = CPU核心数 + 1
- IO密集型:核心线程数 = CPU核心数 * 2
- 混合型:根据实际压测调整
计算公式:
线程数 = CPU核心数 * (1 + 等待时间/计算时间)
示例:
- CPU核心数:8
- 等待时间:90ms(IO操作)
- 计算时间:10ms
- 线程数 = 8 * (1 + 90/10) = 80
8.4 如何避免死锁?
答案要点:
- 按顺序加锁:所有线程按相同顺序获取锁
- 使用tryLock:设置超时时间,获取失败则放弃
- 减少锁的持有时间:尽快释放锁
- 使用并发工具类:如ConcurrentHashMap
8.5 ThreadLocal原理和应用场景?
原理:
// 每个Thread对象都有一个ThreadLocalMap
class Thread {
ThreadLocal.ThreadLocalMap threadLocals;
}
// ThreadLocal的get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = t.threadLocals;
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
return (T)e.value;
}
}
return setInitialValue();
}
应用场景:
- 数据库连接管理:
public class ConnectionManager {
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<>();
public static Connection getConnection() {
Connection conn = connectionHolder.get();
if (conn == null) {
conn = DriverManager.getConnection(url);
connectionHolder.set(conn);
}
return conn;
}
public static void closeConnection() {
Connection conn = connectionHolder.get();
if (conn != null) {
conn.close();
connectionHolder.remove();
}
}
}
- 用户上下文传递:
public class UserContext {
private static ThreadLocal<User> userHolder = new ThreadLocal<>();
public static void setUser(User user) {
userHolder.set(user);
}
public static User getUser() {
return userHolder.get();
}
public static void clear() {
userHolder.remove();
}
}
注意事项:使用完必须remove(),否则内存泄漏!
总结
核心要点回顾
-
线程基础:
- 优先使用线程池,避免直接new Thread
- 理解线程生命周期和状态转换
-
线程安全:
- 简单场景用synchronized
- 高性能场景用原子类
- 复杂场景用ReentrantLock
-
线程池:
- CPU密集型:线程数 = CPU核心数
- IO密集型:线程数 = CPU核心数 * 2
- 必须监控线程池状态
-
并发工具:
- CountDownLatch:等待多个线程完成
- CyclicBarrier:循环栅栏
- Semaphore:限流
- CompletableFuture:异步编排
-
性能优化:
- 减少锁的粒度
- 使用无锁数据结构
- 批量操作减少竞争
实战建议
-
开发阶段:
- 代码review关注线程安全
- 使用FindBugs等工具检测并发问题
- 编写并发测试用例
-
测试阶段:
- 压力测试验证并发性能
- 使用JMeter模拟高并发场景
- 监控线程池和内存使用
-
生产环境:
- 配置线程池监控告警
- 定期分析线程dump
- 关注CPU和内存指标
学习资源推荐
-
书籍:
- 《Java并发编程实战》
- 《Java并发编程的艺术》
- 《深入理解Java虚拟机》
-
实践项目:
- 实现一个线程安全的LRU缓存
- 开发一个高性能的任务调度系统
- 构建一个并发限流组件
-
持续学习:
- 关注JDK新版本的并发特性
- 学习虚拟线程(JDK 21+)
- 研究响应式编程(Reactor、RxJava)
附录:常用命令速查
JVM诊断命令
# 查看Java进程
jps -l
# 查看JVM参数
jinfo -flags <pid>
# 查看堆内存使用
jmap -heap <pid>
# 导出堆内存快照
jmap -dump:live,format=b,file=heap.hprof <pid>
# 查看线程堆栈
jstack <pid> > thread.txt
# 查看GC情况
jstat -gc <pid> 1000 10
# 实时监控
jconsole
jvisualvm
线程状态分析
# 统计线程状态
jstack <pid> | grep "java.lang.Thread.State" | sort | uniq -c
# 查找BLOCKED线程
jstack <pid> | grep -A 10 "BLOCKED"
# 查找WAITING线程
jstack <pid> | grep -A 10 "WAITING"
作者寄语:并发编程是Java开发的核心技能,需要理论结合实践。建议从简单场景入手,逐步深入,多写代码多调试。遇到问题不要慌,善用工具分析,一定能成为并发编程高手!
如果本文对您有帮助,欢迎点赞收藏!有问题欢迎评论区交流讨论。
关键词:Java多线程、并发编程、线程池、synchronized、ReentrantLock、ThreadLocal、CompletableFuture、死锁、性能优化
原创声明:本文为作者原创,基于多年实战经验总结,转载请注明出处。
更多推荐
所有评论(0)