授课语音

Java并发编程介绍

1. 介绍

在实际开发中,Java的并发编程被广泛应用,它能够有效地利用系统资源,提升程序的性能和响应能力。并发可以通过多线程、多进程、协程和异步编程等方式实现。

进程与线程

  • 进程:进程是操作系统中正在运行的程序实例。每个进程有独立的地址空间和系统资源。

    • 进程间通信(IPC)相对复杂,通常使用管道、消息队列和共享内存等机制。
    • 进程切换和资源使用的开销较大,但一个进程的崩溃不会影响其他进程。
    • 适用于独立应用程序,比如浏览器和文本编辑器。
  • 线程:线程是进程中的执行单元。

    • 一个进程可以包含多个线程,这些线程共享进程的地址空间和资源。
    • 线程间通信相对简单,切换和资源使用开销较小,但仍然涉及内核态的上下文切换。
    • 适用于处理并发任务。

并发与并行

  • 并发:多个线程或任务在同一时间段内交替执行。
  • 并行:多个线程或任务在同一时刻同时执行,这需要硬件的支持,如多核处理器。

生命周期

  • 进程的生命周期包括:

    • 新建(操作系统分配资源并初始化)、
    • 就绪(等待操作系统调度)、
    • 运行(CPU上执行指令)、
    • 阻塞(等待某些事件完成)、
    • 结束(执行完毕或由于错误终止)。

    进程管理包括

    • 创建(例如使用fork)、
    • 调度(操作系统调度算法,比如轮询和优先级调度)、
    • 通信(管道、消息队列、共享内存、信号量等)、
    • 终止(操作系统释放并清理资源)。
  • 线程的生命周期包括:

    • 新建(线程被创建但未执行)、
    • 就绪(等待操作系统调度)、
    • 运行(CPU上执行指令)、
    • 阻塞(等待同步锁或其他资源)、
    • 等待(通过wait进入等待状态)、
    • 死亡(线程执行完成或因异常终止)。

    线程管理包括

    • 创建(继承Thread类或实现Runnable接口)、
    • 调度(JVM和操作系统结合管理)、
    • 同步(使用synchronized等机制协调对共享资源的访问)、
    • 线程池(提供高效复用线程和任务调度机制)。

图示 Image___1397757896014449415===c75e2a184e24c7b75453efeda018455a===1_1.png___

线程创建方式

  • 继承Thread类:适合简单场景,但Java是单继承的,限制了不能继承其他类。
  • 实现Runnable接口:更加灵活,避免了Java的单继承限制,实际开发中推荐使用这种方式。

2. 代码案例

进程代码案例

package com.zhilitech;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class ProcessDemo {
    public static void main(String[] args) {
        try {
            // 创建并启动一个进程,该进程尝试运行一个不存在的程序,以模拟异常
            ProcessBuilder processBuilder = new ProcessBuilder("java", "NonExistentProgram");
            Process process = processBuilder.start();

            // 读取进程的错误流,获取异常信息
            BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            String line;
            while ((line = errorReader.readLine()) != null) {
                System.err.println("进程错误: " + line); // 输出进程的错误信息
            }

            // 等待进程结束,并获取退出码
            int exitCode = process.waitFor();
            System.out.println("进程结束,退出码: " + exitCode);

            /*
             * 输出示例:
             * 进程错误: 错误: 找不到或无法加载主类 NonExistentProgram
             * 进程错误: 原因: java.lang.ClassNotFoundException: NonExistentProgram
             * 进程结束,退出码: 1
             */
        } catch (IOException e) {
            // 捕获并处理输入输出异常
            System.err.println("IO 异常发生: " + e.getMessage());
        } catch (InterruptedException e) {
            // 捕获并处理进程等待被中断的异常
            System.err.println("进程被中断: " + e.getMessage());
        } catch (Exception e) {
            // 捕获其他异常
            System.err.println("发生异常: " + e.getMessage());
        }
    }
}

线程代码案例

package com.zhilitech;

import java.util.concurrent.*;

// 共享数据类,用于线程间的同步和通信
class SharedData {
    private String data;
    // volatile修饰符确保变量的可见性,防止指令重排序,但不能保证原子性,适用于简单的同步场景
    private volatile boolean available = false;

    // 生产者线程调用的方法
    public synchronized void produce(String data) throws InterruptedException {
        while (available) {
            wait(); // 阻塞,直到数据被消费
        }
        this.data = data; // 设置共享数据
        available = true; // 标记数据可用
        notify(); // 通知消费者线程
    }

    // 消费者线程调用的方法
    public synchronized String consume() throws InterruptedException {
        while (!available) {
            wait(); // 阻塞,直到数据被生产
        }
        available = false; // 标记数据已消费
        notify(); // 通知生产者线程
        return data; // 返回消费的数据
    }
}

// 继承 Thread 类创建生产者线程
class ProducerThread extends Thread {
    private SharedData sharedData;

    public ProducerThread(SharedData sharedData) {
        this.sharedData = sharedData;
    }

    @Override
    public void run() {
        String[] dataToProduce = {"数据1", "数据2", "数据3"};
        try {
            for (String data : dataToProduce) {
                System.out.println("生产者线程" + Thread.currentThread().getName() + " 生产了: " + data);
                sharedData.produce(data); // 生产数据
                Thread.sleep(100); // 模拟生产时间
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

// 继承 Thread 类创建消费者线程
class ConsumerThread extends Thread {
    private SharedData sharedData;

    public ConsumerThread(SharedData sharedData) {
        this.sharedData = sharedData;
    }

    @Override
    public void run() {
        try {
            for (int i = 0; i < 3; i++) {
                String data = sharedData.consume(); // 消费数据
                System.out.println("消费者线程" + Thread.currentThread().getName() + " 消费了: " + data);
                Thread.sleep(200); // 模拟消费时间
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

// 实现 Runnable 接口创建生产者线程
class ProducerRunnable implements Runnable {
    private SharedData sharedData;
    private String[] dataToProduce;

    public ProducerRunnable(SharedData sharedData, String... dataToProduce) {
        this.sharedData = sharedData;
        this.dataToProduce = dataToProduce;
    }

    @Override
    public void run() {
        try {
            for (String data : dataToProduce) {
                System.out.println("生成者线程" + Thread.currentThread().getName() + " 生产了: " + data);
                sharedData.produce(data); // 生产数据
                Thread.sleep(100); // 模拟生产时间
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // 设置中断状态
            e.printStackTrace();
        }
    }
}

// 实现 Runnable 接口创建消费者线程
class ConsumerRunnable implements Runnable {
    private SharedData sharedData;

    public ConsumerRunnable(SharedData sharedData) {
        this.sharedData = sharedData;
    }

    @Override
    public void run() {
        try {
            for (int i = 0; i < 3; i++) {
                String data = sharedData.consume(); // 消费数据
                System.out.println("消费者线程" + Thread.currentThread().getName() + " 消费了: " + data);
                Thread.sleep(200); // 模拟消费时间
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // 设置中断状态
            e.printStackTrace();
        }
    }
}

public class ThreadDemo {

    public static void main(String[] args) {
        // 创建共享数据对象,用于线程间的同步和通信
        SharedData sharedData = new SharedData();

        // 方法一:继承 Thread 类创建线程
        Thread producerThread1 = new ProducerThread(sharedData);
        Thread consumerThread1 = new ConsumerThread(sharedData

);

        // 启动线程
        producerThread1.start();
        consumerThread1.start();

        // 方法二:实现 Runnable 接口创建线程
        Thread producerThread2 = new Thread(new ProducerRunnable(sharedData, "数据4", "数据5", "数据6"));
        Thread consumerThread2 = new Thread(new ConsumerRunnable(sharedData));

        // 启动线程
        producerThread2.start();
        consumerThread2.start();

        try {
            // 等待线程结束
            producerThread1.join();
            consumerThread1.join();
            producerThread2.join();
            consumerThread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("开始线程池的测试");
        // 自定义线程池
        int poolSize = 4; // 线程池大小
        ExecutorService executorService = Executors.newFixedThreadPool(poolSize);

        // 提交任务到线程池
        executorService.submit(new ProducerRunnable(sharedData, "数据1", "数据2", "数据3"));
        executorService.submit(new ConsumerRunnable(sharedData));
        executorService.submit(new ProducerRunnable(sharedData, "数据4", "数据5", "数据6"));
        executorService.submit(new ConsumerRunnable(sharedData));

        // 关闭线程池
        executorService.shutdown();
        try {
            // 等待线程池中所有任务完成
            if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
                executorService.shutdownNow(); // 超时则强制关闭
            }
        } catch (InterruptedException e) {
            executorService.shutdownNow(); // 线程被中断时强制关闭
            Thread.currentThread().interrupt(); // 恢复中断状态
        }

        /*
         * 示例输出:
         * 生成者线程Thread-2 生产了: 数据4
         * 生产者线程Thread-0 生产了: 数据1
         * 消费者线程Thread-1 消费了: 数据4
         * 消费者线程Thread-3 消费了: 数据1
         * 生产者线程Thread-0 生产了: 数据2
         * 生成者线程Thread-2 生产了: 数据5
         * 消费者线程Thread-1 消费了: 数据2
         * 消费者线程Thread-3 消费了: 数据5
         * 生产者线程Thread-0 生产了: 数据3
         * 生成者线程Thread-2 生产了: 数据6
         * 消费者线程Thread-1 消费了: 数据3
         * 消费者线程Thread-3 消费了: 数据6
         * 开始线程池的测试
         * 生成者线程pool-1-thread-1 生产了: 数据1
         * 生成者线程pool-1-thread-3 生产了: 数据4
         * 消费者线程pool-1-thread-2 消费了: 数据1
         * 消费者线程pool-1-thread-4 消费了: 数据4
         * 生成者线程pool-1-thread-3 生产了: 数据5
         * 生成者线程pool-1-thread-1 生产了: 数据2
         * 消费者线程pool-1-thread-2 消费了: 数据5
         * 消费者线程pool-1-thread-4 消费了: 数据2
         * 生成者线程pool-1-thread-3 生产了: 数据6
         * 生成者线程pool-1-thread-1 生产了: 数据3
         * 消费者线程pool-1-thread-2 消费了: 数据6
         * 消费者线程pool-1-thread-4 消费了: 数据3
         */
    }
}
去1:1私密咨询

系列课程: