Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/379.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Swing多线程。我的GUI冻僵了_Java_Multithreading_Swing - Fatal编程技术网

Java Swing多线程。我的GUI冻僵了

Java Swing多线程。我的GUI冻僵了,java,multithreading,swing,Java,Multithreading,Swing,免责声明:我并没有将我的程序用于任何恶意目的,即使它的名字是Spambot。我只是用它来练习 编辑:我的问题是,如果我按下一个按钮,GUI将冻结,因此在第一个按钮完成其工作之前,我无法按下另一个按钮。我该怎么做 我创建了一个类(SpambotGUI),它基本上是一个包含3个jbutton的JFrame。下面是它的代码: public class SpambotGUI extends JPanel implements ActionListener { private static fin

免责声明:我并没有将我的程序用于任何恶意目的,即使它的名字是Spambot。我只是用它来练习

编辑:我的问题是,如果我按下一个按钮,GUI将冻结,因此在第一个按钮完成其工作之前,我无法按下另一个按钮。我该怎么做

我创建了一个类(SpambotGUI),它基本上是一个包含3个jbutton的JFrame。下面是它的代码:

public class SpambotGUI extends JPanel implements ActionListener {
    private static final long serialVersionUID = 1L;
    static JButton button1 = new JButton("Spam first file");
    static JButton button2 = new JButton("Spam second file");
    static JButton button3 = new JButton("Stop");

    public SpambotGUI() throws AWTException {
        button1.addActionListener(this);
        button2.addActionListener(this);
        button3.addActionListener(this);
        button1.setActionCommand("spam1");
        button2.setActionCommand("spam2");
        button3.setActionCommand("stop");
        button1.setMnemonic(KeyEvent.VK_F7);
        button2.setMnemonic(KeyEvent.VK_F8);
        button3.setMnemonic(KeyEvent.VK_F9);
        button3.setToolTipText("Stop the program");
        add(button1, BorderLayout.WEST);
        add(button2, BorderLayout.CENTER);
        add(button3, BorderLayout.SOUTH);

    }

    public void actionPerformed(ActionEvent e) {
        System.out.println(java.awt.EventQueue.isDispatchThread());
        if ((e.getActionCommand()).equals("spam1")) {
            try {
                Spambot.Start("data/spambotLines1.txt");
            } catch (FileNotFoundException | AWTException | InterruptedException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        } else if ((e.getActionCommand()).equals("spam2")) {
                   try {
                       Spambot.Start("data/spambotLines2.txt");
                   } catch (FileNotFoundException | AWTException | InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                   }
               } else if ((e.getActionCommand()).equals("stop")) {
                          Spambot.stopped = true;
                          Spambot.thread.interrupt();
                      }

    }

    public static void CreateGUI() throws AWTException {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        SpambotGUI buttons = new SpambotGUI();
        buttons.setOpaque(true);
        frame.setContentPane(buttons);

        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String args[]) throws Exception {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    CreateGUI();
                } catch (AWTException e) {
                    e.printStackTrace();
                }

            }
        });
    }

}
我还有一个
Spambot
类,它包括以下内容:在
Start
方法中,我有一个循环,包含不相关的内容和thread.Sleep-s(我创建了一个名为thread的新线程(),这就是为什么它在
SpambotGUI
中用小写字母t拼写的原因),循环应该一直运行,直到
Spambot
中的
stopped
布尔值为false。如果我按下GUI中的
Stop
按钮,后者将设置为false。问题是当
Start
中的循环运行时,我无法单击GUI中的任何按钮。之后我在互联网上读了这篇文章,我得出结论,我应该在这里使用多线程

问题是,我只是不知道它应该如何工作。我尝试在我的
Spambot
类中实现
Runnable
,然后从
SpambotGUI
调用
run()
方法,但没有任何改变

有人知道我应该在这里做什么吗

编辑:这里是
Spambot
类的一部分:

public class Spambot{

    private static Robot robot;
    public static Thread thread = new Thread();
    public static boolean stopped = false;

    public static void main(String... args) throws Exception {

    }

    public static void Start(String path) throws AWTException, InterruptedException, FileNotFoundException {
        Scanner input = new Scanner(new FileReader(path));
        Spambot keyboard = new Spambot();
        Random rand = new Random();
        robot.keyPress(KeyEvent.VK_ALT);
        thread.sleep(150);
        robot.keyPress(KeyEvent.VK_TAB);
        thread.sleep(150);
        robot.keyRelease(KeyEvent.VK_TAB);
        robot.keyRelease(KeyEvent.VK_ALT);
        thread.sleep(500);
        while (input.hasNextLine() && !stopped) {
            keyboard.type(input.nextLine());
            thread.sleep(rand.nextInt(1500)+1000);
            robot.keyPress(KeyEvent.VK_ENTER);
            robot.keyRelease(KeyEvent.VK_ENTER);
        }
        input.close();
    }


    public Spambot() throws AWTException {
        Spambot.robot = new Robot();
    }

    public Spambot(Robot robot) {
        Spambot.robot = robot;
    }
}

您应该在任何GUI应用程序中使用的不是多线程,而是事件驱动编程。这意味着永远不要在事件处理程序中执行长时间运行的循环,永远不要在一个事件处理程序中调用
Thread.sleep

相反,必须在Swing计时器上调度延迟的GUI操作,并且必须将循环体放入您提交给计时器的处理程序中


如果您没有延迟的操作,但确实是一个长时间运行的任务(这意味着您需要进行大量计算或等待I/O),只有这样,您才需要一个后台线程来完成这项工作。在这种情况下,您可以使用
SwingWorker
将任务的结果传回GUI。

在任何GUI应用程序中,您应该使用的不是多线程,而是事件驱动编程。这意味着永远不要在事件处理程序中执行长时间运行的循环,永远不要在一个事件处理程序中调用
Thread.sleep

相反,必须在Swing计时器上调度延迟的GUI操作,并且必须将循环体放入您提交给计时器的处理程序中


如果您没有延迟的操作,但确实是一个长时间运行的任务(这意味着您需要进行大量计算或等待I/O),只有这样,您才需要一个后台线程来完成这项工作。在这种情况下,您将使用
SwingWorker
将任务的结果传回GUI。

我认为您必须了解,您的程序逻辑和
GUI
绝不应该在同一个线程上运行。当您的程序忙于执行任务时,您不希望您的
GUI
冻结。
Java
解决这个问题的方法是
事件调度线程(EDT)。
你这样做:

public static void main(String args[]) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            //Your GUI code here
        }
    });
}

有关EDT的更多信息,请参见此处:

我认为您必须了解,您的程序逻辑和
GUI
不应在同一线程上运行。当您的程序忙于执行任务时,您不希望您的
GUI
冻结。
Java
解决这个问题的方法是
事件调度线程(EDT)。
你这样做:

public static void main(String args[]) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            //Your GUI code here
        }
    });
}

有关EDT的更多信息,请参见此处:

您没有使用任何线程,而只是使用Thread.sleep()方法。这只是一个正常的睡眠程序。因此,在start1和start2中完成操作之前,GUI将被阻止

您没有使用任何线程,而只是使用Thread.sleep()方法。这只是一个正常的睡眠程序。因此,在start1和start2中完成操作之前,GUI将被阻止

您可能实际上必须启动一个新的
线程
,因此阻塞操作不会对应用程序GUI造成太大影响。但是,更新GUI中的操作应该由原始事件调度线程执行

在这里,主要的问题似乎是使用
Thread.sleep()
。当在事件分派线程中执行时,将导致GUI变得不负责任(在完成事件侦听器代码的执行之前不会接受您的输入或重画)。但是,如果在其他线程中使用Thread.sleep()则是可以接受的(这不会冻结您的GUI)

怎么做 首先:在单独的线程中启动阻塞处理代码

public void actionPerformed(ActionEvent e) {
    if ((e.getActionCommand()).equals("spam1")) {
        new Thread(){
            @Override
            public void run() {
                try {
                    Spambot.Start("data/firstfile.txt");
                } catch (FileNotFoundException | InvocationTargetException | 
                        AWTException | InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }.start();
    }
    // ... rest of conditions

}
其次,延迟之间的每个GUI更新都应该在事件调度线程中完成

EventQueue.invokeAndWait(new Runnable(){
    public void run() {
        robot.keyPress(KeyEvent.VK_ALT);
    };
});
由于所有更新都是在
robot.keyPress()
调用中完成的,因此一个好的选择可能是封装并在方法中重用。请注意,内部类中使用的局部变量和参数应定义为final(因此它们在方法的stackframe之外可用)

编辑:Oops。我和SwingWorker搞错了。实际上可能已经足够了。


注意:Swing中有一些帮助器组件,可以使我们免于复杂且容易出错的线程处理。实际上,您可能会使用一个函数,其中被重写的
doInBackground()
方法(在工作线程中)遍历文件,执行暂停,并发送击键(调用
publish(Integer)
),由EDT在被重写的
进程(列表)中处理
方法。

您可能实际上必须启动一个新的
线程
,因此阻塞操作不会对应用程序GUI造成太大影响。但是,更新GUI中的操作应该由原始事件调度线程执行

作为,