写了个下载任务,界面卡成幻灯片,同事一句开个线程就完事。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 没问题;成批任务、要限流或要返回值,就该换线程池,别硬写。