day22——Java多线程编程全面指南:从基础到高级应用

发布于:2025-07-17 ⋅ 阅读:(18) ⋅ 点赞:(0)

本文系统总结了Java多线程编程的核心知识,涵盖线程创建、线程安全、线程通信、线程池等关键技术,帮助开发者掌握高并发编程的核心能力。

一、多线程基础概念

1.1 线程与多线程

  • 线程(Thread):程序内部的一条执行流程
  • 单线程程序:只有一条执行流程的程序
  • 多线程:软硬件上实现的多条执行流程技术(由CPU调度执行)

多线程技术在现代应用中至关重要,尤其是在高并发场景如:

  • 消息通信系统
  • 电商平台(淘宝、京东)
  • 文件上传/下载服务
  • 实时数据处理系统

1.2 线程的创建方式

方式一:继承Thread 类
在这里插入图片描述
在这里插入图片描述

方式二:实现Runnable接口
在这里插入图片描述

在这里插入图片描述
方式二的匿名内部类写法:

/**
 * 目标:掌握多线程创建方式二的匿名内部类写法。
 */
public class ThreadTest2_2 {
    public static void main(String[] args) {
        // 1、直接创建Runnable接口的匿名内部类形式(任务对象)
        Runnable target = new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    System.out.println("子线程1输出:" + i);
                }
            }
        };
        new Thread(target).start();

        // 简化形式1:
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    System.out.println("子线程2输出:" + i);
                }
            }
        }).start();

        // 简化形式2:
        new Thread(() -> {
                for (int i = 1; i <= 5; i++) {
                    System.out.println("子线程3输出:" + i);
                }
        }).start();

        for (int i = 1; i <= 5; i++) {
            System.out.println("主线程main输出:" + i);
        }
    }

方式三:实现Callable接口

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Java提供三种线程创建方式,各有适用场景:

创建方式 实现步骤 优点 缺点
继承Thread类 1. 定义子类继承Thread
2. 重写run()方法
3. 创建线程对象
4. 调用start()
编码简单 无法继承其他类
实现Runnable接口 1. 定义任务类实现Runnable
2. 重写run()方法
3. 创建任务对象
4. 将任务交给Thread处理
可继承其他类,扩展性强 无法直接返回结果
实现Callable接口 1. 定义任务类实现Callable
2. 重写call()方法
3. 封装为FutureTask对象
4. 交给Thread执行
可返回结果,扩展性强 编码复杂

关键代码示例:

// 方式1:继承Thread
class MyThread extends Thread {
//重写run方法
    @Override
    public void run() {
        System.out.println("Thread running");
    }
}

// 方式2:实现Runnable
Runnable task = () -> System.out.println("Runnable running");
new Thread(task).start();

// 方式3:实现Callable
Callable<String> callable = () -> "Result";
FutureTask<String> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
System.out.println(futureTask.get()); // 获取返回值

1.3 Thread类核心方法

方法 作用 注意事项
setName(String) 设置线程名称 便于调试和日志跟踪
getName() 获取线程名称 默认名称为Thread-序号
currentThread() 获取当前执行线程的引用 静态方法
sleep(long millis) 让线程休眠指定毫秒 不释放锁
join() 等待该线程执行结束 常用于线程顺序执行
interrupt() 中断线程 非强制停止,需配合异常处理

二、线程安全与同步机制

2.1 线程安全问题

当多个线程同时操作共享资源且存在修改操作时,可能导致数据不一致。

经典案例:银行取款问题

// 共享账户
public class Account {
    private double balance = 100000; // 初始余额10万
    
    public void drawMoney(double amount) {
        if(balance >= amount) {
            balance -= amount; // 问题可能发生在此处
        }
    }
}

当两个线程同时执行取款操作时:

  1. 线程A检查余额足够(100000 > 100000)
  2. 线程B检查余额足够(100000 > 100000)
  3. 线程A扣款,余额变为0
  4. 线程B扣款,余额变为-100000

2.2 线程同步解决方案

在这里插入图片描述
加锁可以实现线程同步,有三个方法:同步代码块、同步方法、lock锁。

2.2.1 同步代码块
public void drawMoney(double amount) {
    synchronized (this) { // 锁对象建议使用共享资源,this正好是共享资源
        if(balance >= amount) {
            balance -= amount;
        }
    }
}

在这里插入图片描述

2.2.2 同步方法

在这里插入图片描述

public synchronized void drawMoney(double amount) {
    if(balance >= amount) {
        balance -= amount;
    }
}
2.2.3 Lock锁

在这里插入图片描述

private final Lock lock = new ReentrantLock();

public void drawMoney(double amount) {
    lock.lock();
    try {
        if(balance >= amount) {
            balance -= amount;
        }
    } finally {
        lock.unlock(); // 放在finally 确保解锁
    }
}

2.3 同步方案对比

方案 适用场景 优点 缺点
同步代码块 需要精细控制同步范围 锁粒度细,性能较好 编码稍复杂
同步方法 整个方法需要同步 编码简洁 锁粒度粗
Lock锁 需要高级功能(如超时控制) 功能强大,可中断 需手动释放锁

三、线程通信与协调(了解)

当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以相互协调,并避免无效的资源争夺。

3.1 生产者-消费者模型

多线程协作的经典模式:

  • 生产者线程:生成数据
  • 消费者线程:消费数据
  • 协调机制
    • 生产者生产完数据后等待,通知消费者
    • 消费者消费完数据后等待,通知生产者

3.2 核心方法

方法 作用
wait() 让当前线程等待并释放锁
notify() 随机唤醒一个等待线程
notifyAll() 唤醒所有等待线程

3.3 代码实现:包子铺案例

class Desk {
    private List<String> list = new ArrayList<>();
    
    // 生产者方法
    public synchronized void put() {
        try {
            while (!list.isEmpty()) {
                wait(); // 有包子就等待
            }
            list.add("包子");
            System.out.println(Thread.currentThread().getName() + "做了一个包子");
            notifyAll(); // 唤醒消费者
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    // 消费者方法
    public synchronized void get() {
        try {
            while (list.isEmpty()) {
                wait(); // 没包子就等待
            }
            String item = list.remove(0);
            System.out.println(Thread.currentThread().getName() + "吃了:" + item);
            notifyAll(); // 唤醒生产者
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

四、线程池高级应用

4.1 线程池优势

  1. 降低资源消耗:复用已创建的线程
  2. 提高响应速度:任务到达可直接执行
  3. 提高线程可管理性:统一分配、监控线程
  4. 防止资源耗尽:通过队列缓冲和拒绝策略

4.2 线程池创建(推荐方式)

在这里插入图片描述
在这里插入图片描述

ExecutorService pool = new ThreadPoolExecutor(
    3,               // 核心线程数 (正式员工)
    5,               // 最大线程数 (正式+临时工)
    8,               // 临时线程存活时间
    TimeUnit.SECONDS, // 时间单位
    new ArrayBlockingQueue<>(5), // 任务队列 (候客区)
    Executors.defaultThreadFactory(), // 线程工厂 (HR部门)
    new ThreadPoolExecutor.AbortPolicy() // 拒绝策略 (客满处理)
);

在这里插入图片描述

4.3 线程池工作原理

未满
已满
未满
已满
未达
已达
新任务
核心线程是否满?
创建核心线程执行
任务队列是否满?
加入任务队列
线程数是否达上限?
创建临时线程
执行拒绝策略

4.4 任务提交方式

在这里插入图片描述
在这里插入图片描述

// 执行Runnable任务
pool.execute(() -> System.out.println("Runnable task"));

// 执行Callable任务并获取结果
Future<String> future = pool.submit(() -> "Callable result");
String result = future.get(); // 阻塞获取结果

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

4.5 线程池配置建议

任务类型 核心线程数公式 说明
CPU密集型 CPU核数 + 1 大量计算,少IO等待
IO密集型 CPU核数 × 2 大量数据库/网络操作
混合型 (线程等待时间/线程CPU时间 + 1) × CPU核数 根据实际比例调整

五、并发理论与线程生命周期

5.1 并发 vs 并行

概念 定义 关键区别
并发(Concurrency) CPU通过时间片轮转交替执行多个线程 逻辑上同时发生
并行(Parallelism) 多个线程在多个CPU核心上真正同时执行 物理上同时发生

5.2 线程生命周期

Java线程的6种状态及其转换:
在这里插入图片描述

状态详解:

  1. NEW:线程创建但未启动
  2. RUNNABLE:可运行状态(包含就绪和运行中)
  3. BLOCKED:等待监视器锁(同步代码块)
  4. WAITING:无限期等待(wait()方法)
  5. TIMED_WAITING:限期等待(sleep()/wait(timeout))
  6. TERMINATED:线程执行完毕

六、总结与最佳实践

6.1 多线程编程核心要点

  1. 线程创建:优先选择实现Runnable或Callable接口
  2. 线程安全:同步范围最小化,推荐使用Lock锁
  3. 线程协调:使用wait/notify实现生产者-消费者模式
  4. 资源管理:始终使用线程池管理线程资源
  5. 状态监控:关注线程生命周期状态转换

6.2 最佳实践

  1. 命名线程:便于调试和问题追踪
    new Thread(task, "Payment-Thread").start();
    
  2. 使用线程局部变量:避免不必要同步
    private static final ThreadLocal<SimpleDateFormat> formatter = 
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
    
  3. 优先使用并发集合:如ConcurrentHashMap、CopyOnWriteArrayList
  4. 避免死锁:按固定顺序获取多个锁
  5. 使用CompletableFuture:简化异步编程(JDK8+)

掌握多线程技术是Java高级开发的必备能力。通过理解线程原理、熟练使用同步机制和线程池,开发者可以构建出高性能、高并发的应用程序,充分利用现代多核处理器的计算能力。


网站公告

今日签到

点亮在社区的每一天
去签到