我已经在循环中使用线程进行了一些广泛的搜索,虽然我理解了单独的线程如何工作的概念,但我仍然无法掌握如何在我的简单应用程序中实现它。
我的应用程序包含一个带有文本框的表单。这个文本框需要在循环迭代后更新一次。它从按下按钮开始,但循环也应该按下一个停止按钮完成。我使用了一个布尔值来跟踪它是否被按下了。
这是我的表单代码:
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建议的推荐,我的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);
}
} }
答案 0 :(得分:3)
更改此行
myMainForm.setTextBox(i + "counter");
进入
final String var = i + "counter";
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
myMainForm.setTextBox(var);
}
});
}
为什么呢?因为您无法在非UI线程中执行UI工作。
答案 1 :(得分:3)
问题是您正在阻止EDT(事件调度线程),阻止UI刷新,直到您的循环结束。
这些问题的解决方案始终相同,使用Swing Timer
或使用SwingWorker
。
以下是使用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();
}
});
}
}
答案 2 :(得分:3)
为了让Tassos的回答起作用,你实际上必须创建一个你没有做过的新线程。只需致电
ThreadTester example = new ThreadTester(2,this);
example.run();
还不够,只需从EDT调用run
方法即可。您需要执行以下操作:
Thread t = new Thread(new ThreadTester(2,this));
t.start();
请参阅Defining and Starting a Thread。
此外,您希望从两个不同的线程(runnable
)修改相同的字段,这是一个错误。您应该阅读有关java并发的更多信息。