Java 多线程知识梳理(1)作者没有四次元口袋的蓝胖日期2026-07-04标签Java, 多线程, 线程基础一、线程的生命周期状态1.1 两种说法5种 vs 6种说法状态数来源传统说法5种操作系统层面的简化模型Java 官方Thread.State6种JDK 中的枚举定义面试时建议先说明 Java 中是 6 种Thread.State 枚举再提一下传统 5 种说法展示你理解得更全面。1.2 传统 5 种状态模型新建New ↓ 调用 start() 就绪Runnable← 被调度 ← 时间片用完 ↓ CPU调度选中 运行Running ├→ 正常结束 → 死亡Dead ├→ wait/sleep/IO阻塞 → 阻塞Blocked │ ↓ 被唤醒/超时 └──────────────────────────┘状态含义新建New创建了 Thread 对象还没调用 start()就绪Runnable调用了 start()等待 CPU 调度运行Running获得了 CPU 时间片正在执行阻塞Blocked暂停执行让出 CPUsleep、wait、IO 等死亡Dead执行完毕或异常退出1.3 Java 官方 6 种状态Thread.State 枚举publicenumState{NEW,// 新建RUNNABLE,// 可运行就绪运行 合并成一个BLOCKED,// 阻塞等锁WAITING,// 等待无期限等待TIMED_WAITING,// 超时等待TERMINATED;// 终止}状态流转图┌─────────────┐ │ NEW │ new Thread() └──────┬──────┘ │ start() ↓ ┌─────────────┐ ┌─────│ RUNNABLE │ ← CPU调度切换 │ │就绪运行│ │ └──┬─────┬────┘ │ │ │ │ │ │ synchronized竞争锁│ │wait()/join()/LockSupport.park() │ │ │无期限等待 ↓ ↓ ↓ ┌─────────┐ ┌───────────┐ │ BLOCKED │ │ WAITING │ └────┬────┘ └─────┬─────┘ │ │ 抢到锁 notify()/notifyAll() │ │ └──────┬──────┘ ↓ ┌──────────────┐ │TIMED_WAITING │ sleep/time-wait/带超时的park └──────┬───────┘ │ 超时/唤醒 ↓ ┌──────────────┐ │ TERMINATED │ 执行完毕或异常退出 └──────────────┘1.4 6 种状态详解① NEW新建线程对象创建好了但还没调用start()方法此时线程在内存里还没启动② RUNNABLE可运行Java 把就绪和运行合并成了这一个状态就绪start() 调用了在等待 CPU 调度运行拿到了 CPU 时间片正在执行从 JVM 视角看“等 CPU和正在运行都是可运行的”所以合并为一个状态③ BLOCKED阻塞等待获取监视器锁synchronized 锁时的状态比如进入 synchronized 块/方法时锁被别人占着就在这等着④ WAITING无期限等待进入了无限期等待需要被别人唤醒触发方法Object.wait()→ 等 notify/notifyAllThread.join()→ 等另一个线程执行完LockSupport.park()→ 等 unpark⑤ TIMED_WAITING超时等待有超时时间的等待时间到了自动醒触发方法Thread.sleep(long ms)Object.wait(long timeout)Thread.join(long millis)LockSupport.parkNanos()/parkUntil()⑥ TERMINATED终止线程执行完了run 方法正常结束或者抛出了未捕获的异常线程一旦终止不能再 start会抛 IllegalThreadStateException1.5 5 种和 6 种的对应关系传统5种Java 6种对应关系新建NEW一对一就绪RUNNABLERUNNABLE 包含了就绪和运行运行RUNNABLE同上阻塞BLOCKED WAITING TIMED_WAITING传统把三种等待/阻塞合并了死亡TERMINATED一对一 常见面试题Q线程有哪几种状态Java Thread.State 枚举定义了 6 种NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。传统操作系统模型说 5 种新建/就绪/运行/阻塞/死亡Java 把就绪和运行合并成了 RUNNABLE把阻塞细分成了 BLOCKED/WAITING/TIMED_WAITING。QRUNNABLE 和 RUNNING 的区别Java 没有单独的 RUNNING 状态RUNNABLE 包含了就绪和运行两种情况。JVM 把线程调度交给操作系统JVM 层面只知道线程在可运行状态具体是在等 CPU 还是正在跑由 OS 决定。QBLOCKED 和 WAITING 的区别BLOCKED 是在等监视器锁synchronized等的是锁WAITING 是主动调用了 wait/join/park 进入的等的是某个条件/通知。二、线程的三种创建方式2.1 方式一继承 Thread 类// 1. 定义类继承 ThreadclassMyThreadextendsThread{Overridepublicvoidrun(){// 线程要执行的任务for(inti0;i10;i){System.out.println(Thread.currentThread().getName(): i);}}}// 2. 使用publicclassDemo{publicstaticvoidmain(String[]args){MyThreadt1newMyThread();MyThreadt2newMyThread();t1.start();// 启动线程注意是 start()不是 run()t2.start();}}优缺点✅ 写法简单❌ 单继承限制继承了 Thread 就不能继承别的类了❌ 任务和线程耦合在一起2.2 方式二实现 Runnable 接口推荐 ✅// 1. 定义类实现 RunnableclassMyRunnableimplementsRunnable{Overridepublicvoidrun(){for(inti0;i10;i){System.out.println(Thread.currentThread().getName(): i);}}}// 2. 使用publicclassDemo{publicstaticvoidmain(String[]args){MyRunnablernewMyRunnable();Threadt1newThread(r,线程1);// 把任务交给 ThreadThreadt2newThread(r,线程2);// 同一个任务可以给多个线程t1.start();t2.start();}}简化写法Lambda// Java 8 可以直接用 LambdanewThread(()-{System.out.println(线程任务);}).start();优缺点✅ 避免了单继承限制✅ 任务和线程分离更灵活✅ 同一个任务可以被多个线程共享❌ run 方法没有返回值不能抛受检异常2.3 方式三实现 Callable 接口 FutureTask有返回值// 1. 定义类实现 CallableclassMyCallableimplementsCallableInteger{OverridepublicIntegercall()throwsException{// 可以有返回值可以抛异常intsum0;for(inti1;i100;i){sumi;}returnsum;}}// 2. 使用publicclassDemo{publicstaticvoidmain(String[]args)throwsException{MyCallablecnewMyCallable();FutureTaskIntegertasknewFutureTask(c);// 包装成 FutureTaskThreadtnewThread(task);// FutureTask 实现了 Runnablet.start();Integerresulttask.get();// 获取返回值阻塞等待System.out.println(结果result);}}优缺点✅ 有返回值✅ 可以抛出受检异常❌ 写法稍复杂需要 FutureTask 包装❌ get() 会阻塞直到线程返回结果2.4 三种方式对比方式继承 Thread实现 Runnable实现 Callable继承限制单继承不灵活无限制无限制返回值无无有抛异常不能抛受检异常不能抛受检异常可以任务共享不行可以可以复杂度简单简单稍复杂推荐场景简单线程大多数场景需要返回值时实际开发中推荐用 Runnable配合 Lambda或 Callable尽量不要继承 Thread。2.5 start() 和 run() 的区别面试经典题start()run()启动线程JVM 会去调用 run 方法只是一个普通的方法调用会新建线程执行在当前线程执行不会新建线程不能重复调用会抛异常可以重复调用ThreadtnewThread(()-{System.out.println(Thread.currentThread().getName());});t.start();// 输出Thread-0新线程中执行t.run();// 输出main在主线程中执行就是个普通方法调用 常见面试题Q线程有哪几种创建方式三种继承 Thread 类、实现 Runnable 接口、实现 Callable 接口配合 FutureTask。推荐用 Runnable 或 Callable避免单继承限制。Qstart() 和 run() 的区别start() 会启动一个新线程由 JVM 调用 run 方法run() 只是普通方法调用不会新建线程。QRunnable 和 Callable 的区别Runnable 是 run() 方法无返回值不能抛受检异常Callable 是 call() 方法有返回值能抛受检异常Callable 需要配合 FutureTask 使用三、Thread 常用方法 ⭐3.1 获取线程信息// 获取当前线程对象静态方法ThreadcurrentThread.currentThread();// 获取线程名Stringnamecurrent.getName();// 获取线程IDlongidcurrent.getId();// 获取/设置线程优先级1~10默认5越高越优先被调度intprioritycurrent.getPriority();current.setPriority(Thread.MAX_PRIORITY);// 10// Thread.MIN_PRIORITY 1// Thread.NORM_PRIORITY 5// Thread.MAX_PRIORITY 10// 判断是否存活booleanalivecurrent.isAlive();// 判断是否是守护线程booleandaemoncurrent.isDaemon();// 获取线程状态Thread.Statestatecurrent.getState();注意优先级只是建议操作系统不一定完全按优先级来。不要用优先级来控制业务逻辑。3.2 线程休眠sleep()// 静态方法让当前线程睡眠指定毫秒数Thread.sleep(1000);// 睡 1 秒// 可以指定纳秒Thread.sleep(1000,500000);// 1秒500微秒特点静态方法作用于当前线程睡眠期间不释放锁抱着锁睡觉睡眠时间到了自动醒来进入就绪状态需要捕获 InterruptedException3.3 等待线程结束join()ThreadtnewThread(()-{// 做一些耗时操作});t.start();t.join();// 主线程等待 t 线程执行完再继续往下走System.out.println(t 线程执行完了);// 带超时的 joint.join(3000);// 最多等 3 秒等不到就不等了特点让调用 join 的线程通常是主线程进入 WAITING 状态等目标线程执行完了才继续底层是 wait() 实现的可以带超时时间经典场景主线程等子线程做完事再继续比如多线程计算汇总结果。3.4 线程中断interrupt()ThreadtnewThread(()-{while(!Thread.currentThread().isInterrupted()){// 正常执行任务System.out.println(工作中...);try{Thread.sleep(1000);}catch(InterruptedExceptione){// sleep 被中断会抛异常同时清除中断标记// 需要自己决定要不要退出System.out.println(被中断了);break;// 退出循环}}});t.start();// 发起中断只是打个标记不是强制杀死t.interrupt();三个相关方法方法作用是否清除标记t.interrupt()给线程 t 打上中断标记-t.isInterrupted()查看线程 t 的中断状态不清除Thread.interrupted()查看当前线程的中断状态清除调用后标记变回 false⚠️ 重要interrupt() 不是强制停止线程只是建议线程停下来线程收到中断后怎么处理是线程自己决定的正确的停止方式用中断标记 协作式停止不要用 stop()已废弃不安全3.5 守护线程setDaemon()ThreadtnewThread(()-{while(true){System.out.println(守护线程在跑...);try{Thread.sleep(1000);}catch(InterruptedExceptione){}}});t.setDaemon(true);// 设置为守护线程必须在 start() 之前设置t.start();特点守护线程是后台服务线程比如 GC 就是守护线程当所有用户线程非守护线程都结束了JVM 就会退出守护线程也会跟着结束守护线程是否执行完不重要只要用户线程都没了就整体退出setDaemon(true)必须在start()之前调用用户线程 vs 守护线程用户线程main 线程、自己 new 的线程默认都是用户线程守护线程后台服务线程JVM 是否退出只看用户线程3.6 让出 CPUyield()// 静态方法当前线程让出 CPU回到就绪状态Thread.yield();特点让当前线程从运行回到就绪让 CPU 重新调度只是礼让不保证一定能让出让出后可能马上又被调度到实际开发中用得很少3.7 方法汇总表方法类型作用释放锁start()实例启动线程-run()实例任务方法不要直接调-sleep(long ms)静态当前线程睡眠❌ 不释放join()实例等待线程结束✅ 释放底层waitinterrupt()实例打中断标记-isInterrupted()实例查看中断状态-interrupted()静态查看并清除中断标记-setDaemon(boolean)实例设置守护线程-yield()静态让出 CPU❌ 不释放currentThread()静态获取当前线程对象-getName/setName实例线程名-getPriority/setPriority实例线程优先级- 常见面试题Qsleep() 和 wait() 的区别所属类不同sleep 是 Thread 的静态方法wait 是 Object 的方法释放锁不同sleep 不释放锁wait 会释放锁使用场景不同sleep 用于暂停执行wait 用于线程间通信唤醒方式不同sleep 时间到自动醒wait 需要 notify/notifyAll 唤醒使用位置不同wait 必须在 synchronized 块中用sleep 不用Q怎么停止一个线程正确方式是用 interrupt() 协作式停止。给线程打中断标记线程自己检查中断状态并决定是否退出。不要用 stop()它会直接杀死线程可能导致数据不一致已经废弃了。Q守护线程是什么和用户线程的区别守护线程是后台服务线程比如 GC。区别在于 JVM 是否退出所有用户线程结束了 JVM 就退出不管守护线程还在不在跑。四、思维导图速览线程基础 ├── 生命周期 │ ├── 传统5种新建/就绪/运行/阻塞/死亡 │ ├── Java 6种Thread.State │ │ ├── NEW新建 │ │ ├── RUNNABLE就绪运行 │ │ ├── BLOCKED等锁 │ │ ├── WAITING无期限等待 │ │ ├── TIMED_WAITING超时等待 │ │ └── TERMINATED终止 │ └── 状态流转路径 ├── 创建方式 │ ├── 继承 Thread 类不推荐单继承限制 │ ├── 实现 Runnable 接口推荐灵活 │ ├── 实现 Callable FutureTask有返回值 │ ├── start() vs run() │ └── Runnable vs Callable └── 常用方法 ├── sleep()静态、不释放锁 ├── join()等待线程结束 ├── interrupt()中断标记协作式停止 ├── setDaemon()守护线程 ├── yield()让出CPU ├── 优先级1~10默认5 └── 经典对比sleep vs wait五、写在最后学习建议线程状态必须背熟6种状态的名字和流转路径是面试必考题画图记最快三种创建方式都要会写Runnable 最常用Callable 知道怎么用、和 Runnable 的区别就行start() vs run()、sleep() vs wait()这两组对比是经典面试题张口就能说interrupt 要理解协作式思想Java 线程是协作式的不是抢占式的interrupt 只是打标记面试高频排序线程有几种状态分别是什么必问start() 和 run() 的区别必问sleep() 和 wait() 的区别必问线程的创建方式Runnable 和 Callable 的区别怎么停止一个线程为什么 stop() 废弃了守护线程是什么