这个问题是关于volatile关键字的用法。我有一个swing应用程序,它显示了一个表,并且有一个单独的线程可以向表模型添加行。根据{{3}},似乎我必须将某些字段标记为volatile,以确保EDT看到对表模型进行的更新。但即使没有使列表易变,更新似乎也有效。以下是我的代码
import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
JFrame frame = new JFrame("FrameDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyTableModel dm = new MyTableModel();
frame.getContentPane().add(new JScrollPane(new JTable(dm)), BorderLayout.CENTER);
new Thread(new Runnable() {
int count = 0;
@Override
public void run() {
while (true) {
dm.addElement(count++);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
frame.pack();
frame.setVisible(true);
}
class Row {
private String name;
private String value;
public Row(String name, String value) {
super();
this.name = name;
this.value = value;
}
}
class MyTableModel extends AbstractTableModel {
private List<Row> rows = new ArrayList<Test.Row>();
@Override
public int getRowCount() {
return rows.size();
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex == 0) {
return rows.get(rowIndex).name;
} else if (columnIndex == 1) {
return rows.get(rowIndex).value;
} else {
throw new IllegalArgumentException();
}
}
private void addElement(int i) {
rows.add(new Row("Name" + i, "Vlaue0" + i));
fireTableRowsInserted(rows.size() - 1, rows.size() - 1);
}
@Override
public String getColumnName(int column) {
return column == 0 ? "Name" : "Value";
}
}
}
在标记rows
变量volatile之前,我想知道为什么我必须这样做,如果我已经工作了。此外,上面提到的问题也提到了synchronized
关键字。我不明白如何使用synchronized
关键字修复线程无法看到最新值的问题。对此的任何解释也表示赞赏。
答案 0 :(得分:1)
在SwingUtilities.invokeAndWait()中包含呼叫dm.addElement(count++);
而不是
来自javadoc
* Causes <code>doRun.run()</code> to be executed synchronously on the
* AWT event dispatching thread. This call blocks until
* all pending AWT events have been processed and (then)
* <code>doRun.run()</code> returns. This method should
* be used when an application thread needs to update the GUI.
* It shouldn't be called from the event dispatching thread.
* Here's an example that creates a new application thread
* that uses <code>invokeAndWait</code> to print a string from the event
* dispatching thread and then, when that's finished, print
* a string from the application thread.
* <pre>
* final Runnable doHelloWorld = new Runnable() {
* public void run() {
* System.out.println("Hello World on " + Thread.currentThread());
* }
* };
*
* Thread appThread = new Thread() {
* public void run() {
* try {
* SwingUtilities.invokeAndWait(doHelloWorld);
* }
* catch (Exception e) {
* e.printStackTrace();
* }
* System.out.println("Finished on " + Thread.currentThread());
* }
* };
* appThread.start();
* </pre>
答案 1 :(得分:0)
但即使没有使列表变得不稳定,更新似乎也有效。
是的,它可能会在99.9%的时间内继续工作。
Swing旨在在Event Dispatch Thread上完成所有更新。这是为了防止从不同的线程完成更新。
在您的情况下,您仍然只在一个线程上完成更新,因此您不太可能遇到问题。但是,如果您确实遇到问题,则无法重现该问题,因为它是随机的,您不希望最大浪费时间调试随机但是。
因此,请确保所有更新都在EDT上完成。有关详细信息,请阅读Concurrency上的Swing教程中的部分。