Java 终止从UNIX命名管道打开输入流时阻塞的线程

Java 终止从UNIX命名管道打开输入流时阻塞的线程,java,unix,named-pipes,Java,Unix,Named Pipes,我使用JavaIO流从Unix命名管道读取命令。程序有一个线程,该线程使用mkfifo创建一个命名管道,然后侦听该管道,允许用户使用echo”命令“>pipe向其发送命令 它在大多数情况下都可以正常工作,除非我的程序必须在没有收到任何命令的情况下终止(例如,在不可恢复的异常情况下)。 如本文所述:FileInputStream构造函数阻塞,直到其他进程打开管道进行写入。这使得线程实现没有机会处理中断并正确终止 我制作了一个简单的程序来展示我的问题。它会侦听输入命令3秒钟,然后尝试(但失败)自行停

我使用JavaIO流从Unix命名管道读取命令。程序有一个线程,该线程使用
mkfifo
创建一个命名管道,然后侦听该管道,允许用户使用
echo”命令“>pipe

向其发送命令 它在大多数情况下都可以正常工作,除非我的程序必须在没有收到任何命令的情况下终止(例如,在不可恢复的异常情况下)。
如本文所述:
FileInputStream
构造函数阻塞,直到其他进程打开管道进行写入。这使得线程实现没有机会处理中断并正确终止

我制作了一个简单的程序来展示我的问题。它会侦听输入命令3秒钟,然后尝试(但失败)自行停止

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Consumer;

/**
 * Read the input named FIFO to dispatch commands to a command processor.
 */
public final class InputReaderThread {

    private static final long LOOP_PERIOD_MS = 100;

    private final Path fifoPath;

    private final Consumer<String[]> processor;

    private final Thread thread;

    /**
     * Constructor.
     *
     * @param fifoPath path to the named FIFO
     * @param processor the command processor
     */
    public InputReaderThread(final Path fifoPath, final Consumer<String[]> processor) {
        this.fifoPath = fifoPath;
        this.processor = processor;

        this.thread = new Thread(this::run);
        this.thread.setName(InputReaderThread.class.getSimpleName());
    }

    /**
     * Start the input reader thread.<br>
     * It is never legal to start a thread more than once. In particular, a thread may not be restarted once it has completed execution.
     *
     * @see Thread#start()
     */
    public final void start() {
        System.out.println("Requesting input reader thread start");
        this.thread.start();
    }

    /**
     * Request the input reader thread to stop.<br>
     * This method does not wait for the thread to terminate, see {@link #join(long)}.
     */
    public final void stop() {
        System.out.println("Requesting input reader thread stop");
        this.thread.interrupt();
    }

    /**
     * Wait for the internal reader thread to terminate.<br>
     * Throws an {@link InterruptedException} on timeout.
     *
     * @param timeout the maximum time to wait, in milliseconds
     * @return {@code true} if the input thread terminated within timeout
     * @throws InterruptedException the current thread was interrupted while waiting, or the timeout was reached
     * @see Thread#join(long)
     */
    public final boolean join(final long timeout) throws InterruptedException {
        System.out.println("Awaiting input reader thread stop");
        this.thread.join(timeout);
        return !this.thread.isAlive();
    }

    private final void run() {
        System.out.println("Input reader thread started");

        try (final FileInputStream is = new FileInputStream(createFifoPipe(this.fifoPath))) {
            final StringBuilder commandBuilder = new StringBuilder();

            System.out.println("Listening to input FIFO");
            while (!Thread.interrupted()) {
                // Avoid reading when there is no data available
                if (is.available() > 0) {
                    final int b = is.read();

                    if (b == '\n') {
                        // The command is complete: process it
                        final String command = commandBuilder.toString().trim();
                        System.out.println("Received command: " + command);
                        this.processor.accept(command.split(" "));

                        // Reset the command builder
                        commandBuilder.setLength(0);
                    } else {
                        // Append the character to the command
                        commandBuilder.append((char) b);
                    }
                } else {
                    // Poll the input FIFO periodically
                    Thread.sleep(LOOP_PERIOD_MS);
                }
            }
        } catch (final IOException e) {
            throw new RuntimeException("An IO exception occurred on agent FIFO", e);
        } catch (@SuppressWarnings("unused") final InterruptedException e) {
            // Handle interruption by terminating the thread
        }

        System.out.println("Input reader thread terminated");
    }

    /**
     * Helper method to create a Unix named FIFO.
     *
     * @param fifoPath the FIFO path
     * @return the File handler to the created FIFO
     * @throws IOException an I/O error occurs
     * @throws InterruptedException the thread was interrupted while waiting for the FIFO creation
     */
    private static final File createFifoPipe(final Path fifoPath) throws IOException, InterruptedException {
        System.out.println("Creating fifo: " + fifoPath);

        final File fifo = fifoPath.toFile();
        if (fifo.exists()) {
            System.err.println("Deleting existing fifo");
            Files.delete(fifoPath);
        }

        final String[] command = new String[] { "mkfifo", fifo.getAbsolutePath() };
        final Process process = new ProcessBuilder(command).start();

        final int returnStatus = process.waitFor();
        if (returnStatus != 0) {
            throw new IOException("Failed to create fifo: " + returnStatus);
        } else {
            System.out.println("Created fifo: " + fifoPath);
        }

        return fifo;
    }
}
运行此程序在命名管道中写入“stop”将显示输入读取器线程在处理中断时正确终止(不确定为什么会记录两次线程终止,但这是另一个问题):

但是,在未写入“stop”命令的情况下运行会显示
FileInputStream
在打开管道时无法处理中断:

$ java -jar NamedPipeTest.jar fifo &
Requesting input reader thread start
Input reader thread started
Creating fifo: fifo
Deleting existing fifo
Created fifo: fifo
Requesting input reader thread stop
Awaiting input reader thread stop
Failed to terminate input reader thread
我想到的事情:

  • 线程在执行IO时不应被设为守护进程(将这两个线程混用似乎是个坏主意)
  • Java NIO API依赖于seek(),这似乎不适合命名管道

我不知道如何才能解决这个问题,我是否错过了什么?谢谢你的帮助

你能解释一下你是如何知道中断不起作用的吗?这是停止阻塞线程的正常方法。起初我尝试了,然后搜索并找到了回答它的链接(由于某种原因,链接没有出现在我的问题中):基本上,FileInputStream构造函数不会处理来自Unix命名管道的IO阻塞时的中断。您必须发布一个显示问题的完整程序。@Armali问题使用完整复制程序更新抱歉,我无法编译此程序-
错误:找不到符号…ICommandProcessor
$ (java -jar NamedPipeTest.jar fifo &) && sleep 2s && echo "stop" > fifo
Requesting input reader thread start
Input reader thread started
Creating fifo: fifo
Deleting existing fifo
Created fifo: fifo
Listening to input FIFO
Received command: stop
Requesting input reader thread stop
Awaiting input reader thread stop
Input reader thread terminated
Stopped successfully
Requesting input reader thread stop
Awaiting input reader thread stop
Stopped successfully
$ java -jar NamedPipeTest.jar fifo &
Requesting input reader thread start
Input reader thread started
Creating fifo: fifo
Deleting existing fifo
Created fifo: fifo
Requesting input reader thread stop
Awaiting input reader thread stop
Failed to terminate input reader thread