Day 1: 线程基础与操作系统原理
今日金句: "理解线程的本质,就是理解操作系统的灵魂。"
📚 课程概述
Day 1 深入探索 Java 并发编程的基础,从操作系统的角度理解线程的本质,掌握多种线程创建方式,并通过实战代码加深理解。
🎯 学习目标
- 深入理解进程与线程的本质区别
- 掌握操作系统线程模型的演进历程
- 熟练掌握 Java 线程的 4 种创建方式
- 理解线程核心属性和生命周期
- 完成进阶实战练习
🧠 理论基础
1. 进程 vs 线程
1.1 概念对比
| 特性维度 | 进程 (Process) | 线程 (Thread) |
|---|---|---|
| 内存分配 | 独立虚拟内存空间 | 共享进程内存空间 |
| 资源占用 | 拥有独立资源 | 共享进程资源 |
| 创建开销 | 重量级,开销大 | 轻量级,开销小 |
| 通信方式 | IPC (管道、消息队列、共享内存) | 直接读写共享数据 |
| 上下文切换 | 涉及虚拟内存切换 | 仅需切换寄存器和栈 |
| 容错能力 | 进程崩溃不影响其他进程 | 一个线程崩溃可能导致整个进程崩溃 |
| 适用场景 | 运行不同程序的隔离环境 | 程序内部并发执行任务 |
1.2 深度理解
进程的本质:进程是操作系统进行资源分配和调度的基本单位,每个进程都有独立的地址空间、数据栈等,所以进程间的数据不共享。
线程的本质:线程是 CPU 调度的基本单位,同一进程中的多个线程共享该进程的地址空间和资源,可以理解为轻量级进程。
1.3 现代操作系统视角
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 类的优势:
- 避免单继承限制,可以继承其他类
- 更符合面向接口编程原则
- 任务逻辑与线程控制分离
- 便于线程池复用
设计模式应用:
- 策略模式:不同的 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 状态转换图
🎯 实战练习
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());
}
}
📝 今日总结
核心要点回顾
-
进程 vs 线程:
- 进程是资源分配单位,线程是 CPU 调度单位
- 线程共享进程资源,进程间资源隔离
- 线程创建和切换开销远小于进程
-
操作系统线程模型:
- 用户级线程:轻量但无法利用多核
- 内核级线程:支持并行但开销较大
- 混合模型:结合两者优点
-
Java 线程创建方式:
- 继承 Thread 类:简单但不推荐(单继承限制)
- 实现 Runnable 接口:推荐使用,灵活性好
- 实现 Callable 接口:支持返回值和异常
- 线程池方式:生产环境推荐,资源管理优秀
-
线程生命周期:
- 6 种状态:NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED
- 状态转换由 JVM 和程序逻辑共同控制
下节预告
明天我们将深入学习 Java 并发核心工具 - 锁机制与内存模型,包括:
- synchronized 关键字深度解析
- ReentrantLock 和 Condition
- volatile 关键字和 happens-before 原则
- Java 内存模型 (JMM) 详解
课后作业
-
理论作业:
- 详细阐述进程和线程的区别及应用场景
- 分析不同操作系统线程模型的优缺点
- 说明 Java 线程池的核心参数配置策略
-
实践作业:
- 实现一个多线程的批量文件处理工具
- 创建一个线程池管理的小型 Web 服务器
- 实现带超时控制的任务调度器
学习提示: 线程是并发编程的基础,理解线程原理对于掌握后续的锁机制、原子操作等高级概念至关重要。建议多实践,加深对线程状态转换和资源管理的理解。