同步问题

Synchronize problems

基本上,我需要我的程序在等待来自主 JFrame 中的 JTextField 以及所有其他元素的输入时暂停,因此它不是弹出窗口。然而,我现在这样做的方式会暂停整个程序,因为我正在使用 while(true) 或类似的东西。

这是我的 GUI 的样子,这样您就可以更好地理解我在说什么;

如您所见,当我输入命令 start 并按回车键时程序卡住了。


这是我初始化同步的方式:

    input = new JTextField();
    inputScrollPane = new JScrollPane(input, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    input.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            if (waitingForInput) {
                synchronized (holder) {
                    holder.add(input.getText());
                    holder.notify();
                }
                waitingForInput = false;
            }

            JTextField sauce = (JTextField) e.getSource();
            appendCommand(sauce.getText());
            sauce.setText("");
        }
    });

这是我在 LinkedStack 上调用通知时发生的情况...

public String getInput(String s) {
    appendInput(s + ": ");
    input.requestFocus();

    synchronized (holder) {
        while (holder.isEmpty())
            //System.out.println("input inside 2"); //STOPS HERE
            try {
                holder.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        System.out.println("input inside 3");

        String nextString = holder.remove(0);

        System.out.println(nextString);
        return nextString;
    }

    /*Document document = getConsoleInput().getDocument();
    Element rootElem = document.getDefaultRootElement();
    int numLines = rootElem.getElementCount();
    Element lineElem = rootElem.getElement(numLines - 2);
    int lineStart = lineElem.getStartOffset();
    int lineEnd = lineElem.getEndOffset();
    try {
        System.out.println(document.getText(lineStart, lineEnd - lineStart));
        return document.getText(lineStart, lineEnd - lineStart);
    } catch (BadLocationException e) {
        e.printStackTrace();
        return "-1";
    } */           //another attempt I had
}

这是按顺序调用的层次结构或方法链:

我初始化 JTextField 的起点:

input = new JTextField();
    inputScrollPane = new JScrollPane(input, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    input.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            if (waitingForInput) {

                synchronized (holder) {
                    holder.add(input.getText());
                    holder.notify();
                }
                waitingForInput = false;
            }

            JTextField sauce = (JTextField) e.getSource();
            appendCommand(sauce.getText()); //<<<
            sauce.setText("");
        }
    });

在 EDT 内部调用的 AppendCommand(String s) 方法

public void appendCommand(String s) {
    appendInput(s);
    if (!testIfValid(s)) return;
    handler = new CommandHandler(getCurrentInstance());
    switch (CommandList.valueOf(s.toUpperCase())) {
        case HELP:
            handler.help();
            break;
        case START:
            handler.start(currentLevelActive);  //<<<<<<<<<<<
            break;
        case STOP:

            break;
        case EXIT:

            break;
        //default:

    }
}

下一个:

  public void start(Level l) {
    level_instance = l;
    if (level_instance != null) {
        level_instance.start();
    } else {
        //CREATE new level
        int width = 0, height = 0;
        boolean go;

        console_instance.waitingForInput(true);
        console_instance.setInputMessage("Enter level width : ");
        InputThread widthThread = new InputThread("width", console_instance);

        while (!widthThread.done) {
        } ///STOPS HERE ETERNALY!!
        width = widthThread.getInputResult();

        ...

输入线程class:

class InputThread extends Thread {
Console console_instance;
String choice;
int inputResult;
boolean done;

public InputThread(String s, Console c) {
    console_instance = c;
    choice = s;
}

@Override
public void run() {
    String s = "";
    console_instance.setWaitingForInput(true);
    do {
        s = console_instance.getInput("Enter " + choice + " of the level"); // <<<<<<<<<<
    } while (!isInt(s));
    inputResult = Integer.parseInt(s);
    done = true;
}

public int getInputResult() {
    return inputResult;
}

private boolean isInt(String s) {
    int i;
    boolean b;
    try {
        i = Integer.parseInt(s);
        b = true;
        if (i < 0) {
            b = false;
        }
    } catch (NumberFormatException ne) {
        b = false;
    }
    return b;
}
}

Console.getInput(String s) 方法:

public String getInput(String s) {
    appendInput(s + ": ");
    input.requestFocus();

    synchronized (holder) {
        while (holder.isEmpty())
            //System.out.println("input inside 2"); //STOPS HERE
            try {
                holder.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        System.out.println("input inside 3");

        String nextString = holder.remove(0);

        System.out.println(nextString);
        return nextString;
    }
}

这就是它在那个循环中停止的地方。然而,这也会停止整个程序。

对于可能的解决方案,我曾尝试隔离 Threads 中的部分代码,但无济于事...如果您可以帮助我修改此代码以使其正常运行或给我另一种方法来实现此目的,那将是太好了。


编辑:正如@markspace 所建议的那样,我尝试实施 SwingWorker,首先是在 EDT 线程中,但由于 UI 仍然挂起,所以它不起作用,然后在 getInput(String s) 方法。对于后者,代码如下所示:

public String getInput(String s) {
    waitingForInput = true;
    appendInput(s + ": ");
    input.requestFocus();
    String result = "";
    synchronized (holder) {

        worker = new SwingWorker<String, String>() {
            @Override
            public String doInBackground() {
                while (holder.isEmpty()) {
                    //System.out.println("input inside 2"); //STOPS HERE
                    try {
                        holder.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                return holder.remove(0);
            }

            @Override
            public void done() {
                String s ="";
                try {
                    s = super.get();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
                System.out.println(s);

            }
        };

        while(!worker.isDone()) {}
        try {
            result = worker.get().toString();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println(result);
        return result;

    }

我设法通过将原始 Map 包装在另一个 Map 中来解决问题,该 Map 在调用包装地图的 put(...) 时调用一个方法。这将它传递给我的另一个 class CommandHandler 并且从那里它非常自我解释。