ZetCode

Java PipedReader 类

最后修改时间:2025 年 4 月 16 日

java.io.PipedReader 类是一个字符流,从管道中读取数据。它必须连接到 PipedWriter 才能形成管道。写入到 PipedWriter 的数据可以从 PipedReader 中读取。

PipedReader 通常用于线程之间的通信。管道提供了一种线程向另一个线程发送数据的方式。管道有一个固定大小的缓冲区,当缓冲区满或空时,操作会阻塞。

PipedReader 类概述

PipedReader 继承自 Reader,并提供基于字符的管道读取功能。关键方法包括读取操作和连接管理。该类对于多个读取器的并发访问不是线程安全的。

public class PipedReader extends Reader {
    public PipedReader();
    public PipedReader(int pipeSize);
    public PipedReader(PipedWriter src);
    public PipedReader(PipedWriter src, int pipeSize);
    public void connect(PipedWriter src);
    public synchronized int read();
    public synchronized int read(char[] cbuf, int off, int len);
    public synchronized void close();
}

上面的代码显示了 PipedReader 提供的关键方法。这些方法允许从连接到 PipedWriter 的管道中读取字符。可以指定管道大小或使用默认值(1024 个字符)。

创建 PipedReader

可以以多种方式创建 PipedReader。您可以创建一个未连接的读取器,稍后连接它,或者创建它时已经连接到写入器。管道大小会影响线程之间可以缓冲的数据量。

Main.java
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        try {
            // Create unconnected PipedReader
            PipedReader reader1 = new PipedReader();
            
            // Create with default pipe size (1024)
            PipedWriter writer1 = new PipedWriter();
            PipedReader reader2 = new PipedReader(writer1);
            
            // Create with custom pipe size (2048)
            PipedWriter writer2 = new PipedWriter();
            PipedReader reader3 = new PipedReader(writer2, 2048);
            
            System.out.println("Created three PipedReader instances");
            
            reader1.close();
            reader2.close();
            reader3.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了创建 PipedReader 的不同方法。第一个创建了一个未连接的读取器。第二个和第三个创建了连接的读取器,分别使用默认和自定义管道大小。完成操作后,请务必关闭读取器以释放资源。

PipedReader 的基本用法

使用 PipedReader 的最简单方法是将其连接到 PipedWriter 并读取字符。当没有数据可用时,读取操作会阻塞。这对于线程通信很有用。

Main.java
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        try {
            PipedWriter writer = new PipedWriter();
            PipedReader reader = new PipedReader(writer);
            
            // Write data in a separate thread
            new Thread(() -> {
                try {
                    writer.write("Hello from PipedWriter!");
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
            
            // Read data from the pipe
            int data;
            while ((data = reader.read()) != -1) {
                System.out.print((char) data);
            }
            
            System.out.println("\nReading complete");
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例显示了线程之间的基本管道通信。写入器线程发送数据,主线程读取数据。read 方法会阻塞,直到有数据可用。当写入器关闭时,管道会自动关闭。

将字符读取到数组中

为了获得更好的性能,一次将多个字符读入一个 char 数组。 这减少了方法调用并提高了效率。 read 方法返回实际读取的字符数。

Main.java
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        try {
            PipedWriter writer = new PipedWriter();
            PipedReader reader = new PipedReader(writer, 4096); // 4KB buffer
            
            // Writer thread
            new Thread(() -> {
                try {
                    String message = "This is a longer message demonstrating " +
                                   "bulk reading from a PipedReader. " +
                                   "Reading multiple characters at once is more efficient.";
                    writer.write(message);
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
            
            // Reader with buffer
            char[] buffer = new char[32];
            int charsRead;
            
            while ((charsRead = reader.read(buffer)) != -1) {
                System.out.print(new String(buffer, 0, charsRead));
            }
            
            System.out.println("\nBulk reading complete");
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了批量读取到字符数组中。管道具有 4KB 的缓冲区大小。读取器一次处理 32 个字符。charsRead 值指示实际读取到数组中的字符数。

稍后连接 PipedReader

PipedReader 可以在创建时未连接,稍后连接到写入器。必须在进行任何读写操作之前建立连接。尝试使用未连接的管道会导致 IOException。

Main.java
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        try {
            // Create unconnected reader
            PipedReader reader = new PipedReader();
            
            // Create writer and connect later
            PipedWriter writer = new PipedWriter();
            
            // Connect them (can also use writer.connect(reader))
            reader.connect(writer);
            
            // Write data in a separate thread
            new Thread(() -> {
                try {
                    writer.write("Connected after creation");
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
            
            // Read data
            int data;
            while ((data = reader.read()) != -1) {
                System.out.print((char) data);
            }
            
            System.out.println("\nReading from late-connected pipe complete");
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例显示了如何在创建后连接 PipedReader。连接可以从任何一端进行。连接后,管道的工作方式与创建时已连接的方式相同。一次只能存在一个连接。

处理管道缓冲区已满的情况

当管道缓冲区已满时,写入操作会阻塞,直到有空间可用。类似地,当缓冲区为空时,读取操作会阻塞。此行为对于正确的线程协调非常重要。

Main.java
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        try {
            // Small pipe buffer (10 chars)
            PipedWriter writer = new PipedWriter();
            PipedReader reader = new PipedReader(writer, 10);
            
            // Writer thread that writes more than buffer can hold
            Thread writerThread = new Thread(() -> {
                try {
                    for (int i = 0; i < 20; i++) {
                        writer.write('A' + (i % 26));
                        System.out.println("Wrote: " + (char)('A' + (i % 26)));
                    }
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            
            // Slow reader thread
            Thread readerThread = new Thread(() -> {
                try {
                    for (int i = 0; i < 20; i++) {
                        Thread.sleep(200); // Slow reading
                        char c = (char) reader.read();
                        System.out.println("Read: " + c);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            
            writerThread.start();
            readerThread.start();
            
            writerThread.join();
            readerThread.join();
            
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此示例演示了管道阻塞行为。当 10 个字符的缓冲区已满时,写入器将阻塞。缓慢的读取器逐渐消耗数据,允许写入器继续。这展示了管道如何自然地同步生产者和消费者线程。

来源

Java PipedReader 类文档

在本文中,我们介绍了 Java PipedReader 类的基本方法和特性。理解这些概念对于在 Java 应用程序中使用字符流实现线程间通信至关重要。

作者

我的名字是 Jan Bodnar,是一位经验丰富的程序员,在该领域拥有多年的经验。我于 2007 年开始撰写编程文章,此后撰写了 1,400 多篇文章和 8 本电子书。凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程