并发编程15日精通训练营:01线程基础与操作系统原理

58 阅读18分钟

Day 1: 线程基础与操作系统原理

今日金句: "理解线程的本质,就是理解操作系统的灵魂。"

📚 课程概述

Day 1 深入探索 Java 并发编程的基础,从操作系统的角度理解线程的本质,掌握多种线程创建方式,并通过实战代码加深理解。

🎯 学习目标

  • 深入理解进程与线程的本质区别
  • 掌握操作系统线程模型的演进历程
  • 熟练掌握 Java 线程的 4 种创建方式
  • 理解线程核心属性和生命周期
  • 完成进阶实战练习

🧠 理论基础

1. 进程 vs 线程

1.1 概念对比
特性维度进程 (Process)线程 (Thread)
内存分配独立虚拟内存空间共享进程内存空间
资源占用拥有独立资源共享进程资源
创建开销重量级,开销大轻量级,开销小
通信方式IPC (管道、消息队列、共享内存)直接读写共享数据
上下文切换涉及虚拟内存切换仅需切换寄存器和栈
容错能力进程崩溃不影响其他进程一个线程崩溃可能导致整个进程崩溃
适用场景运行不同程序的隔离环境程序内部并发执行任务
1.2 深度理解

进程的本质:进程是操作系统进行资源分配和调度的基本单位,每个进程都有独立的地址空间、数据栈等,所以进程间的数据不共享。

线程的本质:线程是 CPU 调度的基本单位,同一进程中的多个线程共享该进程的地址空间和资源,可以理解为轻量级进程。

1.3 现代操作系统视角

image.png

2. 操作系统线程模型

2.1 用户级线程 (User-Level Thread)

核心特征

  • 线程管理由用户空间线程库完成
  • 内核不知道线程的存在,只看到进程
  • 线程切换在用户空间完成,不需要内核参与

优势

  • 线程切换速度快,无需陷入内核
  • 线程管理灵活,可自定义调度算法
  • 跨平台性好,不依赖具体操作系统

劣势

  • 进程中的任何一个线程阻塞,整个进程都会阻塞
  • 无法利用多核 CPU 的优势
  • 同一时间只能有一个线程在运行
// 伪代码示例:用户级线程库
typedef struct {
    void (*start_routine)(void *);
    void *arg;
    void *stack_ptr;
    int state;
} uthread_t;

// 用户级线程创建
int uthread_create(uthread_t *thread, void (*start_routine)(void *), void *arg) {
    // 在用户空间分配栈和线程控制块
    thread->stack_ptr = malloc(STACK_SIZE);
    thread->start_routine = start_routine;
    thread->arg = arg;
    thread->state = READY;
    return 0;
}

// 用户级线程调度(用户空间)
void uthread_schedule() {
    // 选择下一个可运行的用户级线程
    // 在用户空间切换栈指针
    // 不需要内核参与
}
2.2 内核级线程 (Kernel-Level Thread)

核心特征

  • 线程创建、销毁、调度都由内核完成
  • 每个线程在内核中都有对应的线程控制块
  • 内核可以直接调度线程到不同 CPU 核心

优势

  • 真正的并行执行,可以利用多核 CPU
  • 一个线程阻塞不会影响其他线程
  • 线程调度由内核统一管理,更加公平

劣势

  • 线程创建和切换开销大,需要陷入内核
  • 线程数量受限于内核资源
  • 可移植性差,依赖具体操作系统
// Linux 内核线程描述符示例
struct task_struct {
    volatile long state;    // -1 不可运行, 0 可运行, >0 已停止
    void *stack;           // 内核栈指针
    pid_t pid;             // 进程ID
    pid_t tgid;            // 线程组ID
    struct mm_struct *mm;  // 内存管理
    struct task_struct *parent; // 父进程
    struct list_head children;   // 子进程链表
    struct files_struct *files;  // 打开的文件
    // ... 更多内核线程信息
};

// 内核线程创建 (简化版)
int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) {
    // 在内核空间创建线程
    // 设置线程栈和入口函数
    // 由内核调度器调度执行
}
2.3 混合型线程模型 (M:1 + 1:1)

核心思想

  • 结合用户级线程和内核级线程的优点
  • 多个用户级线程映射到少数内核级线程
  • 用户级线程库负责用户线程调度
  • 内核负责内核线程调度

现代实现

  • Windows: 纤程 (Fiber) + 线程
  • Linux: NPTL (Native POSIX Thread Library)
  • Java JVM: 根据不同平台采用不同策略
// JVM 线程模型抽象
public class JVMThreadModel {
    // 用户级线程 (Green Thread)
    private static class GreenThread {
        private ThreadState state;
        private Object stack;
        private Runnable task;

        public void start() {
            // 用户空间调度
            scheduleGreenThread(this);
        }
    }

    // 内核级线程映射
    private static class KernelThreadMapping {
        private Thread nativeThread;  // 原生线程
        private List<GreenThread> greenThreads;  // 映射的绿色线程

        public void schedule() {
            // 调度用户级线程到内核线程执行
        }
    }
}
2.4 模型对比总结
特性用户级线程内核级线程混合型线程
创建开销小 (用户空间)大 (内核调用)中等
切换开销小 (用户空间)大 (内核调用)小 (用户空间)
并行能力❌ 单核✅ 多核✅ 多核
阻塞影响❌ 阻塞整个进程✅ 不影响其他线程✅ 不影响其他内核线程
系统调用复杂简单中等
实现复杂度简单复杂最复杂

💻 Java 线程实现

1. 继承 Thread 类

1.1 基础实现
/**
 * 方式一:继承 Thread 类创建线程
 * 适用于简单的线程创建场景
 */
public class MyThread extends Thread {

    private final String threadName;

    public MyThread(String threadName) {
        this.threadName = threadName;
    }

    @Override
    public void run() {
        System.out.println("线程 " + threadName + " 开始执行...");

        try {
            // 模拟耗时任务
            for (int i = 0; i < 5; i++) {
                System.out.println(threadName + " 执行步骤 " + i);
                Thread.sleep(1000);  // 休眠1秒
            }
        } catch (InterruptedException e) {
            System.out.println(threadName + " 被中断");
            Thread.currentThread().interrupt();  // 恢复中断状态
        }

        System.out.println("线程 " + threadName + " 执行完成");
    }

    public static void main(String[] args) {
        System.out.println("主线程开始执行");

        // 创建多个线程
        MyThread thread1 = new MyThread("Thread-1");
        MyThread thread2 = new MyThread("Thread-2");

        // 启动线程
        thread1.start();  // 注意:调用 start() 而不是 run()
        thread2.start();

        System.out.println("主线程继续执行");

        try {
            // 等待子线程完成
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println("主线程执行完成");
    }
}
1.2 深度解析

重要概念

  • start() 方法:JVM 会创建新线程并调用 run() 方法
  • run() 方法:线程的执行逻辑,直接调用不会创建新线程
  • 线程优先级:1-10,默认为 5,但不保证严格的优先级调度

线程命名规则

  • 建议使用有意义的名称,便于调试和监控
  • 可以使用线程池的线程工厂统一命名

2. 实现 Runnable 接口

2.1 基础实现
/**
 * 方式二:实现 Runnable 接口创建线程
 * 解决了 Java 单继承的限制,推荐使用
 */
public class MyRunnable implements Runnable {

    private final String taskName;
    private final int iterations;

    public MyRunnable(String taskName, int iterations) {
        this.taskName = taskName;
        this.iterations = iterations;
    }

    @Override
    public void run() {
        System.out.println("任务 " + taskName + " 开始执行 (线程: " +
                          Thread.currentThread().getName() + ")");

        for (int i = 0; i < iterations; i++) {
            try {
                // 模拟计算密集型任务
                long result = fibonacci(i + 10);
                System.out.println(taskName + " - 步骤 " + i + ": fibonacci(" +
                                 (i + 10) + ") = " + result);

                Thread.sleep(500);
            } catch (InterruptedException e) {
                System.out.println(taskName + " 被中断");
                Thread.currentThread().interrupt();
                break;
            }
        }

        System.out.println("任务 " + taskName + " 执行完成");
    }

    // 计算斐波那契数列(模拟计算密集型任务)
    private long fibonacci(int n) {
        if (n <= 1) return n;
        return fibonacci(n - 1) + fibonacci(n - 2);
    }

    public static void main(String[] args) {
        System.out.println("=== Runnable 接口示例 ===");

        // 创建任务
        Runnable task1 = new MyRunnable("计算任务1", 3);
        Runnable task2 = new MyRunnable("计算任务2", 3);

        // 创建线程并执行任务
        Thread worker1 = new Thread(task1, "计算线程-1");
        Thread worker2 = new Thread(task2, "计算线程-2");

        // 设置线程优先级
        worker1.setPriority(Thread.MAX_PRIORITY);
        worker2.setPriority(Thread.MIN_PRIORITY);

        worker1.start();
        worker2.start();

        // Lambda 表达式创建 Runnable (Java 8+)
        Thread lambdaThread = new Thread(() -> {
            System.out.println("Lambda 线程执行中...");
            for (int i = 0; i < 3; i++) {
                System.out.println("Lambda 步骤 " + i);
                try {
                    Thread.sleep(800);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }, "Lambda-Worker");

        lambdaThread.start();

        // 等待所有线程完成
        try {
            worker1.join();
            worker2.join();
            lambdaThread.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println("所有任务执行完成");
    }
}
2.2 优势分析

相比继承 Thread 类的优势

  1. 避免单继承限制,可以继承其他类
  2. 更符合面向接口编程原则
  3. 任务逻辑与线程控制分离
  4. 便于线程池复用

设计模式应用

  • 策略模式:不同的 Runnable 实现代表不同的执行策略
  • 命令模式:Runnable 对象封装了执行命令

3. 实现 Callable 接口

3.1 基础实现
import java.util.concurrent.*;

/**
 * 方式三:实现 Callable 接口创建线程
 * 支持带返回值的任务执行
 */
public class MyCallable implements Callable<String> {

    private final String taskName;
    private final int delay;

    public MyCallable(String taskName, int delay) {
        this.taskName = taskName;
        this.delay = delay;
    }

    @Override
    public String call() throws Exception {
        System.out.println("Callable 任务 " + taskName + " 开始执行...");

        // 模拟耗时操作
        Thread.sleep(delay);

        // 执行计算并返回结果
        String result = taskName + " 执行完成,耗时 " + delay + "ms";
        System.out.println("Callable 任务 " + taskName + " 即将返回结果");

        return result;
    }

    public static void main(String[] args) {
        System.out.println("=== Callable 接口示例 ===");

        // 创建 ExecutorService
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 创建多个 Callable 任务
        Callable<String> task1 = new MyCallable("任务-1", 2000);
        Callable<String> task2 = new MyCallable("任务-2", 1000);
        Callable<String> task3 = new MyCallable("任务-3", 3000);

        try {
            // 提交任务并获取 Future 对象
            Future<String> future1 = executor.submit(task1);
            Future<String> future2 = executor.submit(task2);
            Future<String> future3 = executor.submit(task3);

            System.out.println("所有任务已提交,等待执行结果...");

            // 获取执行结果
            String result1 = future1.get();  // 阻塞等待结果
            String result2 = future2.get();
            String result3 = future3.get();

            System.out.println("任务1结果: " + result1);
            System.out.println("任务2结果: " + result2);
            System.out.println("任务3结果: " + result3);

            // 带超时的获取结果
            Future<String> timeoutTask = executor.submit(
                new MyCallable("超时任务", 5000));

            try {
                String timeoutResult = timeoutTask.get(2, TimeUnit.SECONDS);
                System.out.println("超时任务结果: " + timeoutResult);
            } catch (TimeoutException e) {
                System.out.println("任务执行超时,取消任务...");
                timeoutTask.cancel(true);  // 中断任务
            }

            // 批量提交任务
            List<Callable<String>> tasks = Arrays.asList(
                new MyCallable("批量任务1", 500),
                new MyCallable("批量任务2", 800),
                new MyCallable("批量任务3", 600)
            );

            List<Future<String>> futures = executor.invokeAll(tasks);

            System.out.println("\n批量任务执行结果:");
            for (int i = 0; i < futures.size(); i++) {
                System.out.println("批量任务" + (i + 1) + ": " + futures.get(i).get());
            }

        } catch (InterruptedException | ExecutionException e) {
            System.err.println("任务执行出错: " + e.getMessage());
            Thread.currentThread().interrupt();
        } finally {
            // 关闭线程池
            executor.shutdown();
            try {
                if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                    executor.shutdownNow();
                }
            } catch (InterruptedException e) {
                executor.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }

        System.out.println("主线程执行完成");
    }
}
3.2 深度特性

Future 接口核心方法

  • get(): 阻塞获取结果
  • get(long timeout, TimeUnit unit): 带超时的获取结果
  • cancel(boolean mayInterruptIfRunning): 取消任务执行
  • isDone(): 检查任务是否完成
  • isCancelled(): 检查任务是否被取消

Callable vs Runnable: | 特性 | Runnable | Callable | |------|----------|----------| | 返回值 | ❌ 无 | ✅ 有 | | 异常处理 | ❌ 只能内部处理 | ✅ 可以抛出受检异常 | | 线程池支持 | ✅ 支持 | ✅ 支持 | | Future 支持 | ❌ 不支持 | ✅ 支持 |

4. 线程池方式

4.1 完整实现
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 方式四:使用线程池创建和管理线程
 * 推荐在生产环境中使用
 */
public class ThreadPoolExample {

    // 自定义线程工厂
    private static class CustomThreadFactory implements ThreadFactory {
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        public CustomThreadFactory(String namePrefix) {
            this.namePrefix = namePrefix;
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, namePrefix + "-thread-" + threadNumber.getAndIncrement());
            t.setDaemon(false);  // 设置为非守护线程
            t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

    // 自定义拒绝策略
    private static class CustomRejectedExecutionHandler implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            System.err.println("任务被拒绝执行: " + r.toString() +
                             " - 线程池: [Active: " + executor.getActiveCount() +
                             ", Queue: " + executor.getQueue().size() + "]");

            // 可以选择将任务放入队列等待后续执行
            try {
                executor.getQueue().put(r);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    public static void main(String[] args) {
        System.out.println("=== 线程池示例 ===");

        // 1. 固定大小线程池
        ExecutorService fixedPool = Executors.newFixedThreadPool(3,
            new CustomThreadFactory("FixedPool"));

        // 2. 缓存线程池
        ExecutorService cachedPool = Executors.newCachedThreadPool(
            new CustomThreadFactory("CachedPool"));

        // 3. 单线程池
        ExecutorService singlePool = Executors.newSingleThreadExecutor(
            new CustomThreadFactory("SinglePool"));

        // 4. 自定义线程池 (推荐)
        ThreadPoolExecutor customPool = new ThreadPoolExecutor(
            2,                              // 核心线程数
            5,                              // 最大线程数
            60L,                           // 空闲线程存活时间
            TimeUnit.SECONDS,              // 时间单位
            new LinkedBlockingQueue<>(10), // 任务队列
            new CustomThreadFactory("CustomPool"), // 线程工厂
            new CustomRejectedExecutionHandler()   // 拒绝策略
        );

        try {
            // 提交任务到固定线程池
            System.out.println("\n--- 固定线程池测试 ---");
            for (int i = 0; i < 5; i++) {
                final int taskId = i;
                fixedPool.submit(() -> {
                    System.out.println("FixedPool 任务 " + taskId + " 执行中 - " +
                                     Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                });
            }

            // 提交任务到缓存线程池
            System.out.println("\n--- 缓存线程池测试 ---");
            for (int i = 0; i < 3; i++) {
                final int taskId = i;
                cachedPool.submit(() -> {
                    System.out.println("CachedPool 任务 " + taskId + " 执行中 - " +
                                     Thread.currentThread().getName());
                    try {
                        Thread.sleep(800);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                });
            }

            // 提交带返回值的任务到自定义线程池
            System.out.println("\n--- 自定义线程池测试 ---");
            Future<String> future1 = customPool.submit(() -> {
                Thread.sleep(2000);
                return "自定义任务完成 - " + Thread.currentThread().getName();
            });

            Future<String> future2 = customPool.submit(() -> {
                Thread.sleep(1000);
                return "另一个自定义任务完成 - " + Thread.currentThread().getName();
            });

            System.out.println("等待自定义任务结果...");
            System.out.println(future1.get());
            System.out.println(future2.get());

            // 监控线程池状态
            monitorThreadPool(customPool);

            // 测试任务拒绝
            System.out.println("\n--- 任务拒绝测试 ---");
            for (int i = 0; i < 20; i++) {  // 提交超过队列容量的任务
                final int taskId = i;
                customPool.submit(() -> {
                    try {
                        Thread.sleep(1500);
                        System.out.println("拒绝测试任务 " + taskId + " 完成");
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                });
            }

        } catch (Exception e) {
            System.err.println("执行出错: " + e.getMessage());
        } finally {
            // 优雅关闭线程池
            shutdownThreadPool(fixedPool, "FixedPool");
            shutdownThreadPool(cachedPool, "CachedPool");
            shutdownThreadPool(singlePool, "SinglePool");
            shutdownThreadPool(customPool, "CustomPool");
        }
    }

    // 监控线程池状态
    private static void monitorThreadPool(ThreadPoolExecutor pool) {
        System.out.println("\n--- 线程池状态监控 ---");
        System.out.println("核心线程数: " + pool.getCorePoolSize());
        System.out.println("最大线程数: " + pool.getMaximumPoolSize());
        System.out.println("当前线程数: " + pool.getPoolSize());
        System.out.println("活跃线程数: " + pool.getActiveCount());
        System.out.println("已完成任务数: " + pool.getCompletedTaskCount());
        System.out.println("队列中任务数: " + pool.getQueue().size());
    }

    // 优雅关闭线程池
    private static void shutdownThreadPool(ExecutorService pool, String poolName) {
        System.out.println("\n关闭 " + poolName + " 线程池...");

        pool.shutdown();  // 停止接受新任务
        try {
            // 等待现有任务完成
            if (!pool.awaitTermination(10, TimeUnit.SECONDS)) {
                System.out.println(poolName + " 未在指定时间内关闭,强制关闭...");
                pool.shutdownNow();  // 强制关闭

                // 再次等待
                if (!pool.awaitTermination(5, TimeUnit.SECONDS)) {
                    System.err.println(poolName + " 线程池未能完全关闭");
                }
            }
        } catch (InterruptedException e) {
            pool.shutdownNow();
            Thread.currentThread().interrupt();
        }

        System.out.println(poolName + " 线程池已关闭");
    }
}
4.2 线程池参数详解

ThreadPoolExecutor 核心参数

public ThreadPoolExecutor(
    int corePoolSize,        // 核心线程数:常驻线程数量
    int maximumPoolSize,     // 最大线程数:线程池最大容量
    long keepAliveTime,      // 空闲线程存活时间
    TimeUnit unit,          // 时间单位
    BlockingQueue<Runnable> workQueue,  // 任务队列
    ThreadFactory threadFactory,        // 线程工厂
    RejectedExecutionHandler handler    // 拒绝策略
)

任务队列类型

  • ArrayBlockingQueue: 有界队列,FIFO
  • LinkedBlockingQueue: 可选有界/无界队列
  • SynchronousQueue: 不存储元素的阻塞队列
  • PriorityBlockingQueue: 优先级队列

拒绝策略

  • AbortPolicy (默认): 抛出 RejectedExecutionException
  • CallerRunsPolicy: 由调用线程执行任务
  • DiscardPolicy: 静默丢弃任务
  • DiscardOldestPolicy: 丢弃队列中最老的任务

🔧 线程核心属性和方法

1. 线程属性详解

import java.util.concurrent.TimeUnit;

/**
 * 线程核心属性和方法详解
 */
public class ThreadPropertiesDemo {

    public static void main(String[] args) {
        System.out.println("=== 线程属性和方法详解 ===");

        // 获取当前线程
        Thread currentThread = Thread.currentThread();
        printThreadInfo("主线程", currentThread);

        // 创建自定义线程
        Thread customThread = new Thread(() -> {
            printThreadInfo("自定义线程", Thread.currentThread());

            // 线程休眠
            try {
                System.out.println("线程将休眠 2 秒...");
                Thread.sleep(2000);
                System.out.println("线程休眠结束");
            } catch (InterruptedException e) {
                System.out.println("线程被中断");
                Thread.currentThread().interrupt();
            }

            // 线程让步
            for (int i = 0; i < 5; i++) {
                System.out.println("计数: " + i);
                if (i == 2) {
                    Thread.yield();  // 让出 CPU 时间片
                }
            }
        }, "Custom-Worker-Thread");

        // 设置线程属性
        customThread.setPriority(Thread.MAX_PRIORITY);
        customThread.setDaemon(false);  // 设置为用户线程

        printThreadInfo("启动前", customThread);

        customThread.start();

        // 主线程等待
        try {
            customThread.join(3000);  // 最多等待 3 秒
            if (customThread.isAlive()) {
                System.out.println("自定义线程仍在运行,主线程不再等待");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        // 线程组操作
        ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
        System.out.println("\n--- 线程组信息 ---");
        System.out.println("线程组名称: " + mainGroup.getName());
        System.out.println("线程组父组: " + mainGroup.getParent().getName());
        System.out.println("活跃线程数: " + mainGroup.activeCount());
        System.out.println("线程组优先级: " + mainGroup.getMaxPriority());

        // 获取所有线程信息
        Thread[] threads = new Thread[mainGroup.activeCount()];
        int threadCount = mainGroup.enumerate(threads);
        System.out.println("\n线程组中的线程:");
        for (int i = 0; i < threadCount; i++) {
            printThreadInfo("  " + i, threads[i]);
        }

        // 线程状态演示
        demonstrateThreadStates();
    }

    // 打印线程信息
    private static void printThreadInfo(String prefix, Thread thread) {
        System.out.println("\n--- " + prefix + " 线程信息 ---");
        System.out.println("线程ID: " + thread.getId());
        System.out.println("线程名称: " + thread.getName());
        System.out.println("线程状态: " + thread.getState());
        System.out.println("线程优先级: " + thread.getPriority());
        System.out.println("是否为守护线程: " + thread.isDaemon());
        System.out.println("是否被中断: " + thread.isInterrupted());
        System.out.println("是否存活: " + thread.isAlive());
        System.out.println("线程组: " + thread.getThreadGroup().getName());
        System.out.println("栈跟踪大小: " + thread.getStackTrace().length);
    }

    // 演示线程状态转换
    private static void demonstrateThreadStates() {
        System.out.println("\n=== 线程状态演示 ===");

        Object lock = new Object();
        Thread waitingThread = new Thread(() -> {
            try {
                synchronized (lock) {
                    System.out.println("等待线程进入 WAITING 状态");
                    lock.wait();  // 进入 WAITING 状态
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("等待线程被唤醒");
        }, "Waiting-Thread");

        Thread sleepingThread = new Thread(() -> {
            try {
                System.out.println("休眠线程进入 TIMED_WAITING 状态");
                Thread.sleep(3000);  // 进入 TIMED_WAITING 状态
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("休眠线程休眠结束");
        }, "Sleeping-Thread");

        Thread blockedThread = new Thread(() -> {
            synchronized (lock) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            System.out.println("阻塞线程执行完成");
        }, "Blocked-Thread");

        // 启动线程
        blockedThread.start();
        waitingThread.start();
        sleepingThread.start();

        // 监控线程状态
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(500);
                System.out.printf("监控 - Blocked: %s, Waiting: %s, Sleeping: %s%n",
                    blockedThread.getState(),
                    waitingThread.getState(),
                    sleepingThread.getState());
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }

        // 唤醒等待线程
        synchronized (lock) {
            lock.notify();
        }

        // 等待所有线程完成
        try {
            blockedThread.join();
            waitingThread.join();
            sleepingThread.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println("所有线程执行完成");
    }
}

2. 线程状态详解

2.1 Thread.State 枚举
状态说明触发条件
NEW新建状态线程被创建但未启动
RUNNABLE可运行状态调用 start() 后,包含 READY 和 RUNNING
BLOCKED阻塞状态等待获取监视器锁
WAITING等待状态调用 wait(), join(), LockSupport.park()
TIMED_WAITING计时等待调用 sleep(), wait(timeout), join(timeout)
TERMINATED终止状态线程执行完成或异常退出
2.2 状态转换图

image.png


🎯 实战练习

1. 基础练习 ⭐

任务: 使用两种不同方式创建线程,实现简单的并发计算

/**
 * 基础练习:多线程计算素数
 */
public class BasicExercise {

    public static void main(String[] args) {
        int range = 1000;

        // 方式一:继承 Thread 类
        Thread primeThread1 = new Thread(() -> {
            System.out.println("Thread 方式 - 开始计算素数...");
            for (int i = 2; i <= range; i++) {
                if (isPrime(i)) {
                    System.out.print(i + " ");
                }
            }
            System.out.println("\nThread 方式 - 计算完成");
        });

        // 方式二:实现 Runnable 接口
        Runnable primeTask = () -> {
            System.out.println("Runnable 方式 - 开始计算素数...");
            for (int i = range + 1; i <= range * 2; i++) {
                if (isPrime(i)) {
                    System.out.print(i + " ");
                }
            }
            System.out.println("\nRunnable 方式 - 计算完成");
        };

        Thread primeThread2 = new Thread(primeTask);

        // 启动线程
        primeThread1.start();
        primeThread2.start();

        try {
            primeThread1.join();
            primeThread2.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println("所有计算任务完成");
    }

    private static boolean isPrime(int n) {
        if (n <= 1) return false;
        if (n <= 3) return true;
        if (n % 2 == 0 || n % 3 == 0) return false;

        for (int i = 5; i * i <= n; i += 6) {
            if (n % i == 0 || n % (i + 2) == 0) {
                return false;
            }
        }
        return true;
    }
}

2. 进阶练习 ⭐⭐

任务: 实现一个简单的生产者-消费者模式,使用线程池管理

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 进阶练习:生产者-消费者模式
 */
public class AdvancedExercise {

    // 使用阻塞队列作为缓冲区
    private static final BlockingQueue<String> queue = new LinkedBlockingQueue<>(10);
    private static final AtomicInteger producerCount = new AtomicInteger(0);
    private static final AtomicInteger consumerCount = new AtomicInteger(0);

    public static void main(String[] args) {
        int producerCount = 3;
        int consumerCount = 2;
        int totalItems = 20;

        ExecutorService executor = Executors.newFixedThreadPool(producerCount + consumerCount);

        // 创建生产者
        for (int i = 0; i < producerCount; i++) {
            executor.submit(new Producer(totalItems / producerCount, i));
        }

        // 创建消费者
        for (int i = 0; i < consumerCount; i++) {
            executor.submit(new Consumer());
        }

        executor.shutdown();

        try {
            if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }

        System.out.println("生产者-消费者程序执行完成");
    }

    // 生产者类
    static class Producer implements Runnable {
        private final int itemsToProduce;
        private final int producerId;

        public Producer(int itemsToProduce, int producerId) {
            this.itemsToProduce = itemsToProduce;
            this.producerId = producerId;
        }

        @Override
        public void run() {
            try {
                for (int i = 0; i < itemsToProduce; i++) {
                    String item = "Item-" + producerId + "-" + i;
                    queue.put(item);  // 如果队列满,会阻塞

                    int producedCount = AdvancedExercise.producerCount.incrementAndGet();
                    System.out.println("生产者 " + producerId + " 生产了: " + item +
                                     " (总生产: " + producedCount + ")");

                    Thread.sleep(100);  // 模拟生产时间
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                System.out.println("生产者 " + producerId + " 被中断");
            }
        }
    }

    // 消费者类
    static class Consumer implements Runnable {
        @Override
        public void run() {
            try {
                while (true) {
                    String item = queue.take();  // 如果队列空,会阻塞

                    int consumedCount = consumerCount.incrementAndGet();
                    System.out.println("消费者消费了: " + item + " (总消费: " + consumedCount + ")");

                    // 模拟消费时间
                    Thread.sleep(200);

                    // 检查是否应该退出
                    if (consumedCount >= 20) {
                        break;
                    }
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                System.out.println("消费者被中断");
            }
        }
    }
}

3. 挑战练习 ⭐⭐⭐

任务: 实现一个多线程的文件搜索工具,支持并发搜索多个目录

import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 挑战练习:并发文件搜索工具
 */
public class ChallengeExercise {

    private static final ExecutorService searchExecutor = Executors.newFixedThreadPool(4);
    private static final List<SearchResult> results = new CopyOnWriteArrayList<>();
    private static final AtomicInteger searchCount = new AtomicInteger(0);

    public static void main(String[] args) {
        String searchPattern = "*.java";  // 搜索模式
        List<Path> searchPaths = Arrays.asList(
            Paths.get("src/main/java"),
            Paths.get("src/test/java"),
            Paths.get("lib")
        );

        System.out.println("=== 并发文件搜索工具 ===");
        System.out.println("搜索模式: " + searchPattern);
        System.out.println("搜索路径: " + searchPaths);

        List<Future<SearchResult>> futures = new ArrayList<>();

        long startTime = System.currentTimeMillis();

        // 并发搜索多个路径
        for (Path searchPath : searchPaths) {
            Future<SearchResult> future = searchExecutor.submit(
                new FileSearchTask(searchPath, searchPattern));
            futures.add(future);
        }

        // 等待所有搜索任务完成
        for (Future<SearchResult> future : futures) {
            try {
                SearchResult result = future.get();
                if (result != null) {
                    results.add(result);
                }
            } catch (InterruptedException | ExecutionException e) {
                System.err.println("搜索任务执行出错: " + e.getMessage());
                Thread.currentThread().interrupt();
            }
        }

        // 统计和显示结果
        displayResults();

        long endTime = System.currentTimeMillis();
        System.out.println("搜索总耗时: " + (endTime - startTime) + "ms");

        searchExecutor.shutdown();
    }

    // 文件搜索任务
    static class FileSearchTask implements Callable<SearchResult> {
        private final Path searchPath;
        private final String pattern;

        public FileSearchTask(Path searchPath, String pattern) {
            this.searchPath = searchPath;
            this.pattern = pattern;
        }

        @Override
        public SearchResult call() {
            System.out.println("开始搜索路径: " + searchPath);

            List<Path> foundFiles = new ArrayList<>();
            int searchedFiles = 0;

            try {
                Files.walkFileTree(searchPath, new SimpleFileVisitor<Path>() {
                    @Override
                    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                        searchedFiles++;
                        if (matchesPattern(file.getFileName().toString())) {
                            foundFiles.add(file);
                            System.out.println("找到文件: " + file);
                        }
                        return FileVisitResult.CONTINUE;
                    }

                    @Override
                    public FileVisitResult visitFileFailed(Path file, IOException exc) {
                        System.err.println("无法访问文件: " + file + " - " + exc.getMessage());
                        return FileVisitResult.CONTINUE;
                    }
                });
            } catch (IOException e) {
                System.err.println("搜索路径出错: " + searchPath + " - " + e.getMessage());
            }

            int count = searchCount.incrementAndGet();
            System.out.println("搜索完成 " + count + ": " + searchPath +
                             " (找到 " + foundFiles.size() + " 个文件,检查 " + searchedFiles + " 个文件)");

            return new SearchResult(searchPath, foundFiles, searchedFiles);
        }

        private boolean matchesPattern(String fileName) {
            // 简单的通配符匹配
            if (pattern.equals("*")) return true;
            if (pattern.startsWith("*.")) {
                String extension = pattern.substring(2);
                return fileName.endsWith("." + extension);
            }
            return fileName.contains(pattern);
        }
    }

    // 搜索结果类
    static class SearchResult {
        private final Path searchPath;
        private final List<Path> foundFiles;
        private final int searchedCount;

        public SearchResult(Path searchPath, List<Path> foundFiles, int searchedCount) {
            this.searchPath = searchPath;
            this.foundFiles = new ArrayList<>(foundFiles);
            this.searchedCount = searchedCount;
        }

        public Path getSearchPath() { return searchPath; }
        public List<Path> getFoundFiles() { return foundFiles; }
        public int getSearchedCount() { return searchedCount; }
    }

    // 显示搜索结果
    private static void displayResults() {
        System.out.println("\n=== 搜索结果汇总 ===");

        int totalFiles = 0;
        int totalSearched = 0;

        for (SearchResult result : results) {
            System.out.println("\n路径: " + result.getSearchPath());
            System.out.println("找到文件: " + result.getFoundFiles().size());
            System.out.println("检查文件: " + result.getSearchedCount());

            totalFiles += result.getFoundFiles().size();
            totalSearched += result.getSearchedCount();

            if (!result.getFoundFiles().isEmpty()) {
                System.out.println("文件列表:");
                for (Path file : result.getFoundFiles()) {
                    System.out.println("  - " + file);
                }
            }
        }

        System.out.println("\n=== 总计 ===");
        System.out.println("总共找到文件: " + totalFiles);
        System.out.println("总共检查文件: " + totalSearched);
        System.out.println("搜索路径数: " + results.size());
    }
}

📝 今日总结

核心要点回顾

  1. 进程 vs 线程

    • 进程是资源分配单位,线程是 CPU 调度单位
    • 线程共享进程资源,进程间资源隔离
    • 线程创建和切换开销远小于进程
  2. 操作系统线程模型

    • 用户级线程:轻量但无法利用多核
    • 内核级线程:支持并行但开销较大
    • 混合模型:结合两者优点
  3. Java 线程创建方式

    • 继承 Thread 类:简单但不推荐(单继承限制)
    • 实现 Runnable 接口:推荐使用,灵活性好
    • 实现 Callable 接口:支持返回值和异常
    • 线程池方式:生产环境推荐,资源管理优秀
  4. 线程生命周期

    • 6 种状态:NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED
    • 状态转换由 JVM 和程序逻辑共同控制

下节预告

明天我们将深入学习 Java 并发核心工具 - 锁机制与内存模型,包括:

  • synchronized 关键字深度解析
  • ReentrantLock 和 Condition
  • volatile 关键字和 happens-before 原则
  • Java 内存模型 (JMM) 详解

课后作业

  1. 理论作业

    • 详细阐述进程和线程的区别及应用场景
    • 分析不同操作系统线程模型的优缺点
    • 说明 Java 线程池的核心参数配置策略
  2. 实践作业

    • 实现一个多线程的批量文件处理工具
    • 创建一个线程池管理的小型 Web 服务器
    • 实现带超时控制的任务调度器

学习提示: 线程是并发编程的基础,理解线程原理对于掌握后续的锁机制、原子操作等高级概念至关重要。建议多实践,加深对线程状态转换和资源管理的理解。