【问题标题】:Java interactive consoleJava 交互式控制台
【发布时间】:2015-12-06 23:36:22
【问题描述】:

我目前正在开发一个 Java 命令行应用程序,我想让它具有交互性。我的意思是,我有 2 个线程:

  • 一个线程将一些信息永久写入标准输出
  • 第二个线程只从标准输入读取,正在等待用户的一些命令

但关键是,当我在第一个线程向标准输出写入内容时输入命令时,这真的很烦人,命令是“cut”。

例如: 我想输入命令“set infolevel 2”,当我输入时,第一个线程输出“一些信息 1234”。

我的控制台输出可能如下所示:

set inf                  // user typing command into stdin
some information 1234    // first thread writes out information
olevel 2                 // continuing reading command from stdin

如果有人能告诉我是否有类似的库/API,那就太棒了。谢谢!

【问题讨论】:

  • 我认为这样做的方法是一次从标准输入读取一个字符(不知道具体怎么做),当从另一个线程进入新行时,发出一个字符返回,打印输出,然后是换行符和当前行字符。
  • 你问的是有两个单独的文本区域,一个用于输入,一个用于输出?如果是这样,请查看stackoverflow.com/questions/12945537/…,了解如何将标准输出重定向到 JTextArea。
  • 您可以使用 JCurses 库在控制台中创建单独的窗口。 sourceforge.net/projects/javacurses
  • 谢谢,我稍后再看看..

标签: java multithreading stdin


【解决方案1】:

我猜你会使用System.out.println 输出到控制台,使用new Scanner(System.in); 读取输入。

基本上System.outPrintStreamSystem.inInputStream 并且是不同的对象(和流),但它们都使用相同的源(我猜你的情况是IDE控制台或命令提示符) 因此,当 2 个或更多线程尝试使用相同的源时,您将得到您提到的行为,因为默认情况下主机环境或用户使用相同的源/控制台。从Java source阅读以下内容:

System.out

/** * “标准”输入流。这个流已经 * 打开并准备好提供输入数据。通常这个流 * 对应于键盘输入或由指定的其他输入源 * 宿主环境或用户。 */

System.in

/** * “标准”输出流。这个流已经 * 打开并准备好接受输出数据。通常这个流 * 对应显示输出或其他输出目的地 * 由宿主环境或用户指定


据我所知,确实没有这样的 API 可以帮助您找到所需的内容,但有几个变通选项:

请注意,恕我直言选项 3 或任何基于同步的类似解决方案是您可能不想要的,因为它会限制您的输出能力

选项 1:

配置不同的控制台设备,然后使用System.console() 访问它。在这种情况下,您将有不同的来源来读取和写入控制台,因此您将无法获得所看到的内容。如果你没有显式配置任何东西,那么默认情况下你会得到System.console() as NULL

选项 2:

不要将输出写入控制台,而是使用文件来写入输出。这将确保您的输入和输出流不会相互混淆。

选项 3:

如果您对控制台的读写来源相同,则同步访问。下面是相同的代码,但请注意,在获取和释放对象锁时,您仍然可以看到一些重叠,但是一旦在System.outSystem.in 上获取或释放锁,您将看不到重叠。

public class Test {

    static Object object = new Object();

    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                while(true){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (object) {
                        Date date = new Date();
                        System.out.println("I am coming from first thread i.e. " + Thread.currentThread().getId() + " : " + date);
                    }
                }
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                Scanner scanner = new Scanner(System.in);
                while(true){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (object) {
                        scanner.next();
                    }
                }
            }
        }.start();
    }

}

【讨论】:

  • 好吧,当我使用相同的来源进出时,我会得到这种行为,这听起来很合乎逻辑。但是现在我尝试了你在“选项1”中所说的,我仍然有同样的行为..
  • 如果您的 System.console() 仍然指向相同的源,即 IDE 的日志查看器或命令提示符,则会发生相同的行为。在这里您需要配置(可能,但我也从未尝试过,所以无法告诉您如何配置)System.console() 以便使用 IDE 日志查看器读取它并使用命令提示符进行写入。基本上你需要的是不同的读写资源。对我来说,选项2是最合理最合理的。
【解决方案2】:

您似乎需要在某种对象上同步两个线程。类似:private Object lockObject = new Object(); Thread t1 = new Thread() { public void run() {synchronized( lockObject ){ //your code } } };

【讨论】:

  • 为什么?那不是我需要的。我只想输入某种“固定”行,无论有多少文本写入标准输出。
  • 我的理解是,当线程 1 接受用户输入时,线程 2 正在向控制台输出一些文本。在这种情况下,只要线程 1 正在侦听用户输入,我就会停止线程 2。在这种情况下,您在线程 1 中读取输入的代码将锁定对象,线程 2 将停止输出。
  • @svasa 很确定关键是线程 2 能够在用户输入命令时输出内容。锁定输出流会失败。
  • 看,线程 2 正忙于输出一些东西,我不想弄乱它,而且我没有锁定输出流,我锁定线程 2 直到线程 1 读入用户文本控制台。如果线程 2 正在打印到控制台,它肯定会在某个时候停止打印用户文本,然后打印自己的输出。这样,您将看到用户文本中的中断。这正是要解决的问题。
【解决方案3】:

您可以按照 svasa 在他的回答中建议的那样使用锁定,或者修改您的代码,以便您拥有一个同时执行输入和输出的线程,而另一个线程执行计算工作。使用ArrayBlockingQueue 将工作线程的输出发送到 I/O 线程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-29
    • 2012-06-24
    • 2014-10-21
    • 2013-09-28
    • 2012-06-23
    • 2023-03-30
    • 2018-01-14
    • 2016-12-27
    相关资源
    最近更新 更多