写了个下载任务,界面卡成幻灯片,同事一句开个线程就完事。Java 里最直接的入口就是 thread类,继承它,或者把 Runnable 丢进去,两行代码把耗时活挪到后台。
两种写法选哪个
new Thread(() -> { System.out.println(Thread.currentThread().getName()); }).start();推荐用 Runnable 或 lambda,因为 Java 是单继承,类一旦继承了 Thread 就没法再继承别的业务父类。需要拿到返回值,就用 Callable 配 FutureTask,比裸写 Thread 好用得多。
start 和 run 千万别写反
调 run 只是普通方法调用,代码还在当前线程里跑,界面照样卡死。只有 start 才会让 JVM 开一条新线程,再由它去执行 run。这个坑新手几乎都踩过,写完发现没并发,先回头看看是不是手滑写成了 run。
几个绕不开的方法
- sleep:让当前线程睡指定毫秒,会释放 CPU,但不会释放它持有的锁。
- join:主线程等这条线程跑完再继续,做结果汇总时常用。
- interrupt:通知线程该停了,配合 isInterrupted 判断,别用已废弃的 stop。
- setDaemon:设成守护线程,主线程结束它跟着结束。
状态是排查卡死的钥匙
线程有 NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED 六种状态。用 jstack 打出来,看到一堆线程停在 BLOCKED,基本就是锁没抢到。优先级 setPriority 只有参考意义,别指望靠它精确调度。
任务一多就该上线程池
一个任务一条 Thread,量一大内存就顶不住,创建销毁也费时间。并发任务多的时候用 ThreadPoolExecutor,复用线程、限制并发数,还能配拒绝策略。生产者消费者也能用 wait 和 notify 手写,但阻塞队列更省心。
- 判断提醒:临时跑一两个后台任务,直接 new Thread 没问题;成批任务、要限流或要返回值,就该换线程池,别硬写。