Java需要原生线程支持,因为JVM依赖操作系统调度线程,java.lang.Thread封装了创建、启动等操作;volatile解决可见性而非原子性;ExecutorService比裸线程更安全可控;synchronized与ReentrantLock在锁机制和使用场景上存在关键差异。

为什么Java需要原生线程支持
因为JVM运行在操作系统之上,而操作系统调度的最小单位是线程;没有语言层面对线程的封装,开发者就得直接调用OS API(比如pthread),既难写又难移植。java.lang.Thread 把创建、启动、中断、等待这些操作标准化了,让多任务逻辑能脱离底层差异表达。
常见错误现象:new Thread(() -> {...}).run() —— 这只是普通方法调用,根本没开启新线程;必须用 start() 才触发JVM新建线程并调用 run()。
- 使用场景:IO阻塞时不想卡住主线程(如读文件、发HTTP请求)
- 参数差异:
Thread(Runnable, String)中的名称字符串会出现在堆栈跟踪里,调试时非常有用 - 性能影响:每个线程默认分配1MB栈空间(HotSpot),开太多线程会导致内存耗尽或上下文切换开销飙升
volatile 关键字解决的是可见性,不是原子性
它告诉JVM:“这个变量的读写不能被重排序,且每次读都要从主内存拿最新值,每次写都要立刻刷回主内存”。但 count++ 这种操作包含“读-改-写”三步,volatile 无法保证这三步不被其他线程穿插执行。
典型误用:volatile boolean flag = false; 配合 while(!flag) Thread.sleep(10); 看似合理,但如果 flag 永远不被设为 true,这段代码就变成忙等+睡眠的混合体,既浪费CPU又延迟响应。
立即学习“Java免费学习笔记(深入)”;
- 正确用途:状态标志(如
running)、双重检查锁里的单例引用 - 替代方案:需要原子更新时用
AtomicInteger、AtomicBoolean - 兼容性注意:JDK 5之前
volatile语义不完整,旧代码迁移要小心
ExecutorService 比裸写 Thread 更适合生产环境
手动 new Thread 容易失控:线程数无上限、异常未捕获、资源不回收。而 Executors.newFixedThreadPool(4) 就把线程生命周期、队列策略、拒绝策略都收口了。
容易踩的坑:Executors.newCachedThreadPool() 在高并发下可能创建海量线程,最终OOM;shutdownNow() 不保证任务一定停止,只是中断正在运行的线程,任务本身得配合 Thread.interrupted() 做响应。
- 推荐组合:
ThreadPoolExecutor+ 自定义RejectedExecutionHandler(比如记录日志后降级处理) - 使用场景:后台定时任务、异步通知、批量数据处理
- 性能提示:核心线程数通常设为 CPU核数 + 1(对计算密集型),IO密集型可适当提高
synchronized 和 ReentrantLock 的关键区别在哪
synchronized 是JVM内置锁,进入/退出自动加解锁,不会忘记释放;ReentrantLock 是API层面的锁,必须显式 lock() / unlock(),且 unlock() 必须放在 finally 块里,否则可能永久阻塞其他线程。
一个常被忽略的细节:synchronized 锁的是对象监视器(monitor),静态方法锁的是类对象,而 ReentrantLock 是独立实例,可以多个锁共存于同一对象上。
- 选 synchronized:逻辑简单、不需要超时或中断等待、追求代码简洁
- 选 ReentrantLock:需要尝试获取锁(
tryLock())、指定公平性、或绑定多个Condition实现精准唤醒 - 注意点:锁粒度太粗(比如整个方法加锁)会严重限制并发度,比锁本身更伤性能











