线程的创建

方式一:继承 Thread 类(不推荐,耦合度高)

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程运行中:" + Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start(); // 启动线程
    }
}

缺点:Java 是单继承,继承 Thread 后不能再继承其他类,扩展性差。

方式二:实现 Runnable 接口(推荐,解耦)

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("线程运行中:" + Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new MyRunnable());
        t1.start();
    }
}

方式三:实现 Callable 接口(支持返回值和异常)

import java.util.concurrent.*;

public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "线程返回值:" + Thread.currentThread().getName();
    }

    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new MyCallable());
        System.out.println(future.get()); // 获取返回值
        executor.shutdown();
    }
}

总结对比表

方式 是否返回值 是否抛异常 是否可继承其他类 使用场景
Thread 简单演示
Runnable 大多数并发任务
Callable 需要返回结果的任务

现代推荐:使用线程池(ExecutorService

ExecutorService pool = Executors.newFixedThreadPool(3);
pool.execute(() -> System.out.println("线程池执行任务"));
pool.shutdown();

日常开发中,优先使用 Runnable + 线程池(ExecutorService),避免直接操作 Thread

线程的生命周期

在 Java 中,线程(Thread)的生命周期由 JVM 和操作系统共同管理,其状态转换遵循 Thread.State 枚举定义的 6 种状态。下面用“时间轴+状态图+代码验证”的方式,一次性讲透。

官方 6 状态图(JDK 源码:java.lang.Thread.State)

状态名 中文含义 触发条件/备注
NEW 新建 刚 new Thread(),还未 start()
RUNNABLE 可运行 start() 后,可能正在 CPU 运行,也可能在等待调度
BLOCKED 阻塞 等待 monitor lock(进入 synchronized 块/方法)
WAITING 无限等待 主动调用 wait()、join()、LockSupport.park() 等,无超时
TIMED_WAITING 限时等待 调用 sleep(time)、wait(time)、join(time)、parkNanos 等,有超时
TERMINATED 终止 run() 执行完毕或异常退出

生命周期时间轴(一张图记住)

代码实时查看状态

public class LifeCycleDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            synchronized (LifeCycleDemo.class) {
                try {
                    LifeCycleDemo.class.wait(); // WAITING
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        });

        System.out.println("1. NEW: " + t.getState()); // NEW
        t.start();
        Thread.sleep(50);
        System.out.println("2. RUNNABLE: " + t.getState()); // RUNNABLE
        synchronized (LifeCycleDemo.class) {
            Thread.sleep(50);
            System.out.println("3. BLOCKED: 主线程拿到锁,子线程阻塞 " + t.getState()); // BLOCKED
            LifeCycleDemo.class.notify();
        }
        Thread.sleep(50);
        System.out.println("4. TERMINATED: " + t.getState()); // TERMINATED
    }
}

易混淆点速记

场景 所属状态 原因说明
Thread.sleep(1000) TIMED_WAITING 不释放锁,限时休眠
obj.wait() WAITING 释放锁,需 notify()
synchronized 竞争锁 BLOCKED 未拿到 monitor
lock.lock() (ReentrantLock) 不是 BLOCKED 在 JDK 层面是 WAITING/TIMED_WAITING,JVM 不感知
yield() 仍是 RUNNABLE 只是提示调度器让出 CPU,状态不变
join() 当前线程进入 WAITING/TIMED_WAITING 等待目标线程结束

Thread.State 枚举定义的 6 种状态中为啥没Running

Thread.State 枚举里确实只有 RUNNABLE 而没有 RUNNING,这并不是遗漏,而是 有意为之

Java 把“正在 CPU 上跑”和“可跑但还没分到 CPU”这两件事合并成了同一个状态:RUNNABLE

OS vs JVM 视角

视角 状态个数 状态名示例 备注
OS 3+ RUNNING, READY, BLOCKED 内核调度器可见
JVM 6 RUNNABLE, BLOCKED, WAITING… 跨平台抽象层
  • RUNNING操作系统内核 里的概念,表示 当前占用 CPU

  • JVM 为了跨平台,把“READY + RUNNING”统一成 RUNNABLE不暴露底层调度细节
    这样无论 Windows、Linux、macOS 调度策略如何,Java 程序看到的状态都一样。

更多推荐