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框架等内容,保持详细的技术解析和代码示例...)
正文到此结束
相关文章
热门推荐
评论插件初始化中...
Loading...
微信扫一扫:分享
微信里点“发现”,扫一下
二维码便可将本文分享至朋友圈。