Java多线程:Thread类核心方法与高并发实践
一、线程实现方式与Thread类本质
在Java中实现多线程的传统方式分为两种:继承Thread类和实现Runnable接口。但从JVM层面看,两者最终都通过Thread类实现。通过反编译可以看到:
public class MyThread extends Thread {
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
// 实际等效于
MyThread thread = new MyThread();
thread.start();
Runnable实现方式通过Thread的构造函数注入:
Thread thread = new Thread(() -> {
System.out.println("Lambda Runnable");
});
关键区别在于面向对象设计原则:接口实现更符合组合优于继承的原则。但底层Thread类的native方法控制着线程的真实生命周期。
二、线程状态机深度剖析
Java线程的6种状态(NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED)实际对应操作系统级线程的不同阶段:
- NEW状态:仅JVM层面的对象初始化
- RUNNABLE状态:包含操作系统层的Ready和Running
- BLOCKED状态:仅适用于synchronized锁竞争
- WAITING系列:涉及LockSupport.park()底层调用
通过jstack工具观察线程状态:
"main" #1 prio=5 os_prio=0 tid=0x00007f... nid=0x15db waiting on condition
三、核心方法原理与陷阱
1. start()方法的双重校验
start()方法包含同步校验逻辑:
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
start0(); // native方法
}
常见错误:重复调用start()导致异常,正确做法是创建新Thread实例。
2. sleep()的精度问题
long start = System.nanoTime();
Thread.sleep(100);
long end = System.nanoTime();
System.out.println("实际休眠:" + (end - start)/1000000 + "ms");
测试发现实际休眠时间受操作系统调度影响,精度无法保证,不适合精确计时。
3. interrupt()的底层实现
中断机制通过native方法实现,调用链:
interrupt() -> interrupt0()(native)
-> pthread_kill()(Linux)
-> SignalDispatcher(SIGINT)
正确处理中断的模板代码:
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
// 阻塞操作
Thread.sleep(1000);
} catch (InterruptedException e) {
// 恢复中断状态
Thread.currentThread().interrupt();
break;
}
}
}
四、线程同步的现代实践
1. synchronized的锁升级过程
- 偏向锁:MarkWord记录线程ID
- 轻量级锁:CAS自旋尝试
- 重量级锁:操作系统互斥量
通过-XX:-UseBiasedLocking关闭偏向锁优化
2. Lock接口的性能对比
测试代码:
ReentrantLock vs synchronized 百万次加锁耗时:
- 单线程:Lock快15%
- 高竞争:Lock快300%
3. StampedLock的乐观读
StampedLock lock = new StampedLock();
long stamp = lock.tryOptimisticRead();
if (!lock.validate(stamp)) {
stamp = lock.readLock();
try {
// 重新读取
} finally {
lock.unlockRead(stamp);
}
}
五、线程池的工程化配置
根据业务场景定制线程池:
new ThreadPoolExecutor(
核心线程数 = CPU密集型:N+1,IO密集型:2N,
最大线程数 = 任务队列长度/平均处理时间,
存活时间 = 根据任务波动频率设置,
工作队列 = 内存控制选择ArrayBlockingQueue,
拒绝策略 = 记录日志后降级处理
);
监控线程池状态:
executor.getQueue().size(); // 当前队列积压
executor.getActiveCount(); // 活动线程数
六、并发问题定位技巧
-
线程Dump分析:
- BLOCKED状态线程查找锁持有者
- WAITING线程检查notify()调用链
-
JFR记录锁定竞争:
jcmd <pid> JFR.start duration=60s filename=recording.jfr
- 异步异常追踪:
executor.setThreadFactory(r -> {
Thread t = new Thread(r);
t.setUncaughtExceptionHandler((thread, ex) -> {
logger.error("Thread {} failed", thread.getName(), ex);
});
return t;
});
七、性能优化实战案例
某交易系统优化过程:
- 原始方案:synchronized方法
- 问题定位:JFR显示锁竞争率78%
- 优化步骤:
- 拆解大同步块
- 使用ConcurrentHashMap分段锁
- 统计类改为LongAdder
- 效果:TPS从1200提升到5600
(后续内容继续深入讲解线程本地存储、原子类原理、ForkJoin框架等内容,保持详细的技术解析和代码示例...)
正文到此结束
相关文章
热门推荐
评论插件初始化中...