3

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

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

这是我的表单代码:

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);
    }

}

如您所见,我按下了一个按钮。当按下按钮时,它会执行另一个名为 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);
    }  
}

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

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

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);
        }
} }
4

3 回答 3

3

更改此行

    myMainForm.setTextBox(i + "counter");

进入

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

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

于 2013-06-14T11:54:43.417 回答
3

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

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

Here is an example of the usage of 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();
            }
        });
    }
}
于 2013-06-14T13:06:30.250 回答
3

为了使 Tassos 的回答起作用,您实际上必须创建一个新线程,而您没有这样做。只需调用

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

还不够,sice 只是run从 EDT 调用方法。您需要执行以下操作:

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

请参阅定义和启动线程

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

于 2013-06-14T13:17:39.130 回答