Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • MacOS
  • Linux
  • Windows
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • MacOS
  • Linux
  • Windows
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java基础

    • Java概述
    • Java语法
    • 面向对象编程
    • Java数组
    • String字符串
    • 异常处理
  • Java集合

    • Java集合概述
    • ArrayList
    • LinkedList
    • HashMap
    • LinkedHashMap
    • HashSet
    • TreeMap
    • Queue&Deque
  • Java并发

    • Java并发概述
    • 线程与进程
    • Thread类与线程生命周期
    • 线程安全
    • synchronized关键字
    • volatile关键字
    • Java内存模型(JMM)
    • 线程间通信
      • 1. 为什么需要线程间通信
      • 2. 线程间通信的方式
        • 2.1 wait()、notify() 和 notifyAll()
        • 2.2 join() 方法
        • 2.3 管道流通信(PipedInputStream 和 PipedOutputStream)
        • 2.4 BlockingQueue
      • 3. 线程间通信的最佳实践
      • 4. 总结
    • 线程池
    • 并发工具类
    • 原子操作类Atomic
    • 并发锁
    • 并发容器
    • ConcurrentHashMap
    • BlockingQueue
    • CopyOnWriteArrayList
    • ThreadLocal
    • Fork/Join框架
    • ScheduledThreadPoolExecutor
    • CompletableFuture
  • Java IO

    • Java IO概述
  • JVM

    • JVM概述
  • Java
  • Java并发
Wray
2024-10-31
目录

线程间通信

线程间通信是指在多线程编程中,多个线程协同工作,相互之间交换数据和信息,以完成一个共同的任务。Java 提供了多种方式来实现线程间的通信,确保线程之间可以正确协调和共享数据。

# 1. 为什么需要线程间通信

在多线程环境中,线程往往需要协同工作。例如,在生产者-消费者模型中,生产者线程生成数据,消费者线程处理数据,这就需要线程之间进行通信,以确保生产和消费的协调一致。线程间通信可以避免资源竞争、死锁等问题,实现线程之间的协作。

# 2. 线程间通信的方式

Java 提供了几种主要的线程间通信方式:

# 2.1 wait()、notify() 和 notifyAll()

这些方法是 Java 提供的最基本的线程间通信机制,它们定义在 Object 类中,必须在同步块或同步方法中使用。

  • wait():调用该方法的线程进入等待状态,释放对象的锁,直到其他线程调用 notify() 或 notifyAll() 将其唤醒。
  • notify():唤醒一个正在等待该对象锁的线程,被唤醒的线程会重新尝试获取锁。
  • notifyAll():唤醒所有正在等待该对象锁的线程。

示例代码:生产者-消费者模型

class SharedResource {
    private int value;
    private boolean available = false;

    public synchronized void produce(int newValue) throws InterruptedException {
        while (available) {
            wait();
        }
        value = newValue;
        available = true;
        notifyAll();
    }

    public synchronized int consume() throws InterruptedException {
        while (!available) {
            wait();
        }
        available = false;
        notifyAll();
        return value;
    }
}

在这个例子中,生产者线程调用 produce() 方法生产数据,如果数据已经被生产而未被消费,线程会调用 wait() 进入等待状态,直到消费者线程调用 consume() 方法消费数据并通知生产者线程。

# 2.2 join() 方法

  • join():Thread 类提供的 join() 方法可以使当前线程等待另一个线程执行结束。这样可以实现线程间的顺序控制,确保一个线程在另一个线程完成后再继续执行。

示例代码:

Thread t1 = new Thread(() -> {
    System.out.println("Thread 1 is running");
});
Thread t2 = new Thread(() -> {
    try {
        t1.join();
        System.out.println("Thread 2 is running after Thread 1");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
t1.start();
t2.start();

在这个例子中,t2 线程调用了 t1.join(),这使得 t2 必须等待 t1 执行完毕之后才能继续运行。

# 2.3 管道流通信(PipedInputStream 和 PipedOutputStream)

  • 管道流:Java 提供了 PipedInputStream 和 PipedOutputStream(或 PipedReader 和 PipedWriter)用于线程间的字节流或字符流通信。
  • 应用场景:管道流适用于一个线程写入数据,另一个线程读取数据的场景。

示例代码:

PipedInputStream input = new PipedInputStream();
PipedOutputStream output = new PipedOutputStream(input);

Thread producer = new Thread(() -> {
    try {
        output.write("Hello from Producer".getBytes());
        output.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
});

Thread consumer = new Thread(() -> {
    try {
        int data;
        while ((data = input.read()) != -1) {
            System.out.print((char) data);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
});

producer.start();
consumer.start();

在这个例子中,生产者线程通过 PipedOutputStream 写入数据,消费者线程通过 PipedInputStream 读取数据。

# 2.4 BlockingQueue

  • BlockingQueue:java.util.concurrent 包提供的 BlockingQueue 是一种线程安全的队列,可以在多线程环境中非常方便地实现生产者-消费者模型。
  • 常见实现:如 ArrayBlockingQueue、LinkedBlockingQueue 等。
  • 阻塞行为:当队列为空时,消费者线程会被阻塞;当队列满时,生产者线程会被阻塞。

示例代码:

BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(10);

Thread producer = new Thread(() -> {
    try {
        queue.put(1);
        System.out.println("Produced: 1");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});

Thread consumer = new Thread(() -> {
    try {
        int value = queue.take();
        System.out.println("Consumed: " + value);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});

producer.start();
consumer.start();

使用 BlockingQueue 可以避免手动控制同步,极大地简化了线程间通信的复杂度。

# 3. 线程间通信的最佳实践

  • 避免死锁:在使用 wait()、notify() 等方式时,必须保证正确的加锁和释放锁顺序,避免线程相互等待导致死锁。
  • 最小化锁的粒度:在使用同步块时,应尽量缩小锁的粒度,减少锁的竞争,提升程序的并发性能。
  • 使用高层并发工具:在可能的情况下,优先使用 Java 并发包中的工具类(如 BlockingQueue、Semaphore 等),以减少低级线程通信的复杂性和错误风险。

# 4. 总结

Java 中的线程间通信机制包括 wait()/notify()、join()、管道流以及 BlockingQueue 等方式。它们各自适用于不同的场景,从最基本的锁机制到高层的并发工具类,Java 提供了丰富的线程间通信手段,帮助开发者实现高效、健壮的多线程程序。理解不同通信机制的特点和适用场景,有助于编写更好的并发代码。

上次更新: 2024/11/01, 13:45:14
Java内存模型(JMM)
线程池

← Java内存模型(JMM) 线程池→

Copyright © 2023-2024 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式