java线程的几种状态是什么?

说实话,讲Java线程这事儿吧,我当年刚接手项目时也是一脸懵。
但摸着石头过河吧,慢慢就清晰了。
就拿我之前做的那个电商系统后台来说,线程状态转换简直是家常便饭。

你想想那个订单处理线程。
创建的时候就是NEW状态,我们用Runnable接口写的,代码块就一行new OrderHandler().start(); 这时候它就乖乖蹲在那儿。
有意思的是,真正让它动起来的是start()方法调用——这时候它蹦到RUNNABLE队列里,但CPU还没给它发牌呢。

我们系统里经常用sleep来模拟等待。
比如处理支付回调,OrderHandler线程会先sleep(5 000),这时候它就变成BLOCKED状态,而且是其他阻塞里那种"主动暂停"的。
说白了,不是被锁住,就是自己说的"我先歇五秒"。
等支付回调真的来了,或者sleep时间到了,它就自动蹦回RUNNABLE,等着CPU再次选中。

最头疼的是wait和notify的配合。
记得有一次做分布式锁,我们用对象锁同步方法,结果忘了在wait时释放锁。
那直接就卡死,排查了好半天才发现是线程在等待队列里没动静。
后来改用Condition,感觉清晰多了。
obj.wait()释放锁这一点,确实容易踩坑。

死亡状态TERMINATED嘛,其实挺明确的。
我们监控后台看到线程突然消失,多半就是run方法跑完了或者抛出未捕获异常。
但有个细节要记住,就算你把那个terminated的线程对象重新start,会报IllegalThreadStateException。
我当时也没想明白为啥,后来查源码才发现,JVM层面线程对象生命周期就结束了,只是对象还在。

对了,thread.sleep(long millis)和yield()的区别特别重要。
sleep会释放锁,但yield呢?它只是把CPU时间片让出来,可能下一秒又被选中。
我们系统里用yield的地方很少,主要是怕CPU又跑回旧线程,增加代码可读性不如直接sleep或者用条件变量。

状态转换图里那个NEW→RUNNABLE(start())→RUNNING(调度选中)→BLOCKED→RUNNABLE→TERMINATED,说白了就是线程的完整一生。
就我跑过的几个项目来看,BLOCKED状态其实占CPU时间最长的。
比如等数据库查询结果、等MQ消息,那种卡等特别消耗资源。
所以现在设计系统,尽量用异步回调代替同步等待,性能好多了。

数据我记得是X左右,但建议你核实下具体数字。
我这边项目里线程阻塞超时的告警,平均每天有几十次,大部分都是因为等待远程API响应。
这块我没亲自跑过最新版本,但这个现象应该普遍存在。

Java线程池系列之-Java线程池底层源码分析系列(三)

上周看这段源码分析。

2 02 3 年3 月1 5 号。

主要讲ThreadPoolExecutor。

execute方法很关键。

核心线程是重点。

队列满了咋办?
就是扩容策略。

我那个朋友问过这个。

cachedThreadPool很特别。

线程数是0到最大。

同步移交队列。

确实适合爬虫。

singleThreadExecutor单线程。

保证顺序执行。

但内存要小心。

keepAliveTime要注意。

线程回收靠这个。

总结一下。

启动策略要记牢。

扩容缩容看条件。

常用线程池特性。

配置参数很重要。

你看着办。