为什么队列不在主线程上更新?

Why isn't the queue updating on the main thread?

我正在打开一个 PowerShell 实例并通过 System.in 向它发送命令,我想将输出收集到一个队列中,这样我就可以在不干扰命令输入的情况下打印结果。然而,队列并没有在主线程上填满,在主线程上休眠只能稍微解决这个问题。我想问题是没有同步队列,但我不确定如何。

package PWST;

import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class PWST {
    public static void main(String[] args) {
        try {
            Scanner in = new Scanner(System.in);
            System.out.print("3[H3[2J > "); // clear console.
            String[] tokens = in.nextLine().split(",");

            Queue<String> stdout = new LinkedList<>();
            Queue<String> stderr = new LinkedList<>();

            Process process = Runtime.getRuntime().exec("powershell.exe");
            new Thread(new SPQ(process.getInputStream(), stdout)).start();
            new Thread(new SPQ(process.getErrorStream(), stderr)).start();
            PrintWriter out = new PrintWriter(process.getOutputStream());
                
            for (String token : tokens) // parse commands (will be expanded to continuously ask for input)
                out.println(token.strip());

            // print powershell output after:
            System.out.println(stdout);
            System.out.println(stderr);

            in.close();
            out.close();
            process.waitFor();
            System.out.println("Shell link closed");
            
        } catch (Exception e) { e.printStackTrace(); }
    }
}
package PWST;

import java.io.InputStream;
import java.util.Queue;

class SPQ implements Runnable {
    private Queue<String> queue;
    private InputStream istrm;

    public SPQ(InputStream istrm, Queue<String> queue) {
        this.istrm = istrm;
        this.queue = queue;
    }

    public void run() {
        try {
            final byte[] buffer = new byte[1024];
            for (int length = 0; (length = istrm.read(buffer)) != -1; )
                queue.add(new String(buffer, 0, length)); // store output in queue
                
        } catch (Exception e) { e.printStackTrace(); }
    }
}

至少有两个问题。

首先,关闭 ScannerPrintWriter 也会关闭它们的不死流(在这种情况下,Scanner 正在从标准输入读取)。这是不可取的,因为,首先,您永远不想关闭 stdin 流,其次,该过程可能尚未完成对流的处理,但更重要的是,我会非常小心地关闭您没有自己创建的流,只是说.

第二个问题是,您试图在 SPQ 实际有时间处理输出之前读取(流的)输出,例如,如果我调整您的代码并使用。 ..

import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.Queue;

public final class Main {
    public static void main(String[] args) throws IOException, InterruptedException {
        new Main();
    }

    public Main() throws IOException, InterruptedException {
        ProcessBuilder pb = new ProcessBuilder("bash", "-c", "ls");
        pb.redirectErrorStream(true);
        System.out.println(">> Start process");
        Process p = pb.start();
        Queue<String> stdout = new LinkedList<>();
        Thread thread = new Thread(new SPQ(p.getInputStream(), stdout));
        System.out.println(">> Start thread");
        thread.start();
        System.out.println(">> Waiting for process to exit");
        p.waitFor();
        System.out.println("<< Process has exited");
        System.out.println(stdout);
    }

    class SPQ implements Runnable {
        private Queue<String> queue;
        private InputStream istrm;

        public SPQ(InputStream istrm, Queue<String> queue) {
            this.istrm = istrm;
            this.queue = queue;
        }

        public void run() {
            System.out.println(">> Started reading stream");
            try {
                final byte[] buffer = new byte[1024];
                for (int length = 0; (length = istrm.read(buffer)) != -1;) {
                    queue.add(new String(buffer, 0, length)); // store output in queue
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("<< Done reading stream");
        }
    }
}

它将输出...

>> Start process
>> Start thread
>> Waiting for process to exit
<< Process has exited
>> Started reading stream
[]
<< Done reading stream

如您所见,我在线程完成流处理之前打印了 Queue 的内容。

如果我在 p.waitFor(); 之后添加 thread.join(); 它将打印...

>> Start process
>> Start thread
>> Waiting for process to exit
>> Started reading stream
<< Done reading stream
<< Process has exited
[contents
of
my
working
directory
which
is
not
very
interesting]

可运行示例...

import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.Queue;

public final class Main {
    public static void main(String[] args) throws IOException, InterruptedException {
        new Main();
    }

    public Main() throws IOException, InterruptedException {
        ProcessBuilder pb = new ProcessBuilder("bash", "-c", "ls");
        pb.redirectErrorStream(true);
        System.out.println(">> Start process");
        Process p = pb.start();
        Queue<String> stdout = new LinkedList<>();
        Thread thread = new Thread(new SPQ(p.getInputStream(), stdout));
        System.out.println(">> Start thread");
        thread.start();
        System.out.println(">> Waiting for process to exit");
        p.waitFor();
        thread.join();
        System.out.println("<< Process has exited");
        System.out.println(stdout);
    }

    class SPQ implements Runnable {
        private Queue<String> queue;
        private InputStream istrm;

        public SPQ(InputStream istrm, Queue<String> queue) {
            this.istrm = istrm;
            this.queue = queue;
        }

        public void run() {
            System.out.println(">> Started reading stream");
            try {
                final byte[] buffer = new byte[1024];
                for (int length = 0; (length = istrm.read(buffer)) != -1;) {
                    queue.add(new String(buffer, 0, length)); // store output in queue
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("<< Done reading stream");
        }
    }
}

请注意 - 我不是 运行 Windows 所以我无法测试 Powershell 我已经完成了我能想到的最小示例,所以我没有向流中写入内容,但从概念上讲,这解决了您似乎遇到的问题