线程的三种创建方式与生命周期及线程池

一.线程的三种创建方式与生命周期

1. 三种创建方式

方式一:继承 Thread(不推荐,单继承限制)

typescript 复制代码
public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 运行");
    }

    public static void main(String[] args) {
        new MyThread().start();
    }
}

方式二:实现 Runnable(推荐,解耦任务和线程)

typescript 复制代码
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 运行");
    }

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

方式三:实现 Callable + FutureTask(带返回值,推荐)

arduino 复制代码
public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(1000);
        return "任务完成";
    }

    public static void main(String[] args) throws Exception {
        FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
        new Thread(futureTask).start();
        
        // 阻塞获取结果
        String result = futureTask.get();
        System.out.println(result);
    }
}

实际开发中优先用 Runnable / Callable ,配合线程池使用,不要直接 new Thread()


2. 线程六种状态

表格

状态 触发条件
NEW 刚创建,未调用 start()
RUNNABLE 调用 start(),等待或正在运行
BLOCKED 等待监视器锁(synchronized)
WAITING 无限期等待,如 wait()join()
TIMED_WAITING 限时等待,如 sleep(1000)wait(1000)
TERMINATED 执行完毕或异常退出

代码查看状态:

scss 复制代码
Thread t = new Thread(() -> {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
System.out.println(t.getState()); // NEW
t.start();
System.out.println(t.getState()); // RUNNABLE / TIMED_WAITING

3. 常用方法速查

表格

方法 作用
start() 启动线程,进入 RUNNABLE
run() 直接调用不会启动新线程,只是普通方法执行
sleep(ms) 当前线程休眠,不释放锁
yield() 让出 CPU,进入就绪态,下次调度可能还是它
join() 等待该线程执行完,再执行当前线程
interrupt() 打断线程,配合 isInterrupted()InterruptedException 使用

二.线程池 ThreadPoolExecutor

1. 为什么要用线程池?

  • 减少线程创建/销毁的开销
  • 控制并发线程数量
  • 便于线程管理和复用

2. 七大核心参数

arduino 复制代码
public ThreadPoolExecutor(
    int corePoolSize,           // 核心线程数(常驻)
    int maximumPoolSize,        // 最大线程数
    long keepAliveTime,         // 非核心线程空闲存活时间
    TimeUnit unit,              // 时间单位
    BlockingQueue<<Runnable> workQueue,  // 任务等待队列
    ThreadFactory threadFactory,        // 线程工厂(自定义线程名)
    RejectedExecutionHandler handler    // 拒绝策略
)

执行流程

  1. 任务进来,当前运行线程 < corePoolSize:创建新线程执行任务
  2. 当前运行线程 ≥ corePoolSize:任务进入 workQueue
  3. 队列满了,且运行线程 < maximumPoolSize:创建临时线程(非核心)执行任务
  4. 运行线程 ≥ maximumPoolSize:触发拒绝策略

3. 四种拒绝策略

表格

策略 行为
AbortPolicy(默认) 直接抛异常 RejectedExecutionException
CallerRunsPolicy 由调用线程(提交任务的线程)自己执行
DiscardPolicy 静默丢弃任务,不抛异常
DiscardOldestPolicy 丢弃队列最老的任务,重试提交

4. 自定义线程池(生产环境推荐)

不要用 Executors 创建线程池! FixedThreadPoolSingleThreadPool 的队列是 LinkedBlockingQueue,允许长度无限,会导致 OOM。

csharp 复制代码
public class ThreadPoolConfig {
    
    public static ThreadPoolExecutor createPool() {
        return new ThreadPoolExecutor(
            4,                              // 核心线程数:CPU密集型 = CPU核数,IO密集型 = 2*CPU核数
            8,                              // 最大线程数
            60L,                            // 空闲线程存活时间
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100),   // 有界队列!防止 OOM
            new ThreadFactoryBuilder().setNameFormat("order-pool-%d").build(), // Guava 工具
            new ThreadPoolExecutor.CallerRunsPolicy() // 满了就让提交者自己跑,起到限流效果
        );
    }
}

提交任务:

ini 复制代码
ThreadPoolExecutor pool = ThreadPoolConfig.createPool();

// 无返回值
pool.execute(() -> System.out.println("执行任务"));

// 有返回值
Future<Integer> future = pool.submit(() -> {
    return 1 + 1;
});
Integer result = future.get(); // 阻塞获取

5. 优雅关闭

scss 复制代码
pool.shutdown();           // 平滑关闭,等待任务执行完
pool.awaitTermination(30, TimeUnit.SECONDS); // 最多等30秒
pool.shutdownNow();        // 立即关闭,返回未执行的任务列表
相关推荐
颜酱8 小时前
07 | 把字段与指标同步到 Qdrant(生成阶段)
前端·人工智能·后端
Larcher9 小时前
从“加载模型”界面到端侧推理:拆解一个 React + WebGPU 大模型 Demo
javascript·后端
Larcher9 小时前
从状态快照到惰性初始化:读懂 React useState 的三个关键场景
javascript·人工智能·后端
lazy H9 小时前
Git clone 怎么用?克隆项目及常见问题完整教程
大数据·git·后端·学习·搜索引擎·github
wang09079 小时前
自己动手写一个spring之aop_1
java·后端·spring
神奇小汤圆10 小时前
IDEA 运行报 Command line is too long?别慌,两招搞定(附原理)
后端
SelectDB10 小时前
Apache Doris 4.1 全面增强 Iceberg:支持 UPDATE、MERGE INTO 与 Iceberg V3
后端
Conan在掘金10 小时前
ArkTS 进阶之道(13):ForEach 循环渲染边界——为啥 build 里不能写 for 循环
后端
妙码生花11 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(四十一):增加管理员账号管理接口
后端·go·gin
用户06782607432711 小时前
APP版本管理全链路(后端设计)
后端