java 使用线程循环更新 JFrame

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17107728/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-01 01:01:26  来源:igfitidea点击:

Using a thread loop to update a JFrame

javamultithreadingswingloopsjframe

提问by Festivejelly

ive done some extensive searching on using threads in a loop and whilst I understand the concept how how seperate threads work, I still cant seem to grasp how to implement it in my simple application.

我对在循环中使用线程进行了一些广泛的搜索,虽然我理解了独立线程如何工作的概念,但我似乎仍然无法掌握如何在我的简单应用程序中实现它。

My application consists of a form with a text box. This textbox needs to be updated once ever iteration of a loop. It starts with the press of a button but the loop should also finish with the press of a stop button. Ive used a boolean value to track if its been pressed.

我的应用程序包含一个带有文本框的表单。此文本框需要在循环迭代后更新一次。它以按下按钮开始,但循环也应以按下停止按钮结束。我使用了一个布尔值来跟踪它是否被按下。

Here is my form code:

这是我的表单代码:

package threadtester;

public class MainForm extends javax.swing.JFrame {

    public MainForm() {
        initComponents();
    }

    private void RunButtonActionPerformed(java.awt.event.ActionEvent evt) {
       ThreadTester.setRunnable(true);
       ThreadTester example = new ThreadTester(2,this);
       example.run();
    }

    private void StopButtonActionPerformed(java.awt.event.ActionEvent evt) {
       ThreadTester.setRunnable(false);
    }


    public static void main(String args[]) {

    java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new MainForm().setVisible(true);
            }
        });
    }

    public void setTextBox(String myString){
    MainTextbox.setText(myString);
    }

}

As you can see I have a button that is pressed. When the button is pressed this executes the code thats in a different class called ThreadTester. Here is the code for that class:

正如你所看到的,我有一个被按下的按钮。当按钮被按下时,它会执行名为 ThreadTester 的不同类中的代码。这是该类的代码:

package threadtester;

import java.util.logging.Level;
import java.util.logging.Logger;

public class ThreadTester implements Runnable
{
    int thisThread;
    MainForm myMainForm;
    private static boolean runnable;
    // constructor
    public ThreadTester (int number,MainForm mainForm)
    {
        thisThread = number;
        myMainForm = mainForm;   
    }

    public void run ()
    {
    for (int i =0;i< 20; i++) {
        if(runnable==false){
           break;
        } 
        System.out.println("I'm in thread " + thisThread + " line " + i);
        myMainForm.setTextBox(i + "counter");
        try {
               Thread.sleep(1000);
        } catch (InterruptedException ex) {
            Logger.getLogger(ThreadTester.class.getName()).log(Level.SEVERE, null, ex);
        }
    } }

    public static void setRunnable(Boolean myValue){
       runnable = myValue;
    }

    public static void main(String[] args) {

        MainForm.main(args);
    }  
}

as you can see the loop has been created on a seperate thread... but the textbox only updates after the loop has finished. Now as far as im aware in my MainForm I created a seperate thread to run the loop on, so I dont understand why its not running? Any guidence would be much appreciated, ive tried looking at examples on stack exchange but I cant seem to get them to fit into my implemntation.

正如您所看到的,循环是在单独的线程上创建的……但文本框仅在循环完成后更新。现在据我所知,在我的 MainForm 中,我创建了一个单独的线程来运行循环,所以我不明白为什么它没有运行?任何指导将不胜感激,我尝试查看有关堆栈交换的示例,但我似乎无法让它们适合我的实现。

With the recommendation suggested by Tassos my run method now looks like this:

根据 Tassos 的建议,我的 run 方法现在看起来像这样:

public void run ()
{
for (int i =0;i< 20; i++) {
    if(runnable==false){
        break;
    }
    System.out.println("I'm in thread " + thisThread + " line " + i);

    final String var = i + "counter";
        java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
                myMainForm.setTextBox(var);
        }
    });


    try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            Logger.getLogger(ThreadTester.class.getName()).log(Level.SEVERE, null, ex);
        }
} }

采纳答案by predi

In order for Tassos' answer to work, you actually have to createan new thread, which you did not do. Simply calling

为了让 Tassos 的答案起作用,您实际上必须创建一个新线程,而您没有这样做。简单地打电话

ThreadTester example = new ThreadTester(2,this);
example.run();

is not enough, sice that just calls the runmethod from EDT. You need to do the following:

仅仅run从 EDT调用方法是不够的。您需要执行以下操作:

Thread t = new Thread(new ThreadTester(2,this));
t.start();

Please refer to Defining and Starting a Thread.

请参阅定义和启动线程

Also, you want modify the same field from two different threads (runnable), which is a bug. You should read more about java concurrency.

此外,您希望从两个不同的线程 ( runnable)修改相同的字段,这是一个错误。您应该阅读有关 Java 并发的更多信息。

回答by Tassos Bassoukos

Change this line

改变这一行

    myMainForm.setTextBox(i + "counter");

into

进入

final String var = i + "counter";
java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
                    myMainForm.setTextBox(var);
        }
    });
}

Why? Because you can't do UI work in non-UI threads.

为什么?因为你不能在非 UI 线程中做 UI 工作。

回答by Guillaume Polet

The problem is that you are blocking the EDT (Event Dispatching Thread), preventing the UI to refresh until your loop is finished.

问题是您正在阻塞 EDT(事件调度线程),阻止 UI 刷新,直到您的循环完成。

The solutions to these issues is always the same, use a Swing Timeror use a SwingWorker.

这些问题的解决方案总是相同的,使用 SwingTimer或使用SwingWorker.

Here is an example of the usage of a SwingWorker:

下面是一个使用 a 的例子SwingWorker

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class TestSwingWorker {

    private JTextField progressTextField;

    protected void initUI() {
        final JFrame frame = new JFrame();
        frame.setTitle(TestSwingWorker.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JButton button = new JButton("Clik me to start work");
        button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                doWork();
            }
        });
        progressTextField = new JTextField(25);
        progressTextField.setEditable(false);
        frame.add(progressTextField, BorderLayout.NORTH);
        frame.add(button, BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);
    }

    protected void doWork() {
        SwingWorker<Void, Integer> worker = new SwingWorker<Void, Integer>() {
            @Override
            protected Void doInBackground() throws Exception {
                // Here not in the EDT
                for (int i = 0; i < 100; i++) {
                    // Simulates work
                    Thread.sleep(10);
                    publish(i); // published values are passed to the #process(List) method
                }
                return null;
            }

            @Override
            protected void process(List<Integer> chunks) {
                // chunks are values retrieved from #publish()
                // Here we are on the EDT and can safely update the UI
                progressTextField.setText(chunks.get(chunks.size() - 1).toString());
            }

            @Override
            protected void done() {
                // Invoked when the SwingWorker has finished
                // We are on the EDT, we can safely update the UI
                progressTextField.setText("Done");
            }
        };
        worker.execute();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TestSwingWorker().initUI();
            }
        });
    }
}