SwingWorker相关问题!!!谢谢各位~ 本帖最后由 frr0717 于 2012-12-20 16:31:40 编辑 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 因为你按钮点击一次就会new一个swingwork thread点击10次当然是new十个啦。你在事件dispatch线程中刷新UI,最好使用SwingUtilties.invokelater 大牛,首先谢谢您上次的解答!!!针对您刚刚的回复,我的问题是:1、我原先也试过改成只new一个UpdateUserWorker实例,然后调用该实例的execute方法,结果还是开了10个线程。那这个怎么解释呢?2、“在事件dispatch线程中刷新UI,最好使用SwingUtilties.invokelater ”这个道理我明白,您是说我哪里没有遵循这个规则了?3、是不是有时候出现的空白,和上图中的异常,和上述2中的问题相关,是应该在EDT上更新GUI组件引起的吗?谢谢!!! SwingWorker 类似往EDT中插入一个任务,因此按一次就新建一个线程。而报错和空白可能是由UpdateUsersWorker updateUsersWorker = new UpdateUsersWorker(in, out, dlm);中的dlm引起,当EDT在渲染list时候,SwingWorker结果CPU时间片,然后清空dlm ,然后EDT接管CPU时间片,继续渲染时候,dlm里面已经没有数据,因此报错。 1L +1@SuppressWarnings("rawtypes")public class UpdateUsersWorker extends SwingWorker<DefaultListModel, String> { private BufferedReader in; private PrintWriter out; private DefaultListModel dlm; public UpdateUsersWorker(BufferedReader in, PrintWriter out, DefaultListModel dlm) { this.in = in; this.out = out; this.dlm = dlm; } @SuppressWarnings("unchecked") @Override protected DefaultListModel doInBackground() throws Exception { System.out.println(getState()); out.println("12");//向服务器发送12号功能请求 dlm.clear();//清空原有数据 String line = null; try { line = in.readLine(); } catch (IOException e1) { e1.printStackTrace(); }//用一个临时变量存储readLine的返回值 while ((!line.equals("END")) && (line != null)) {//String的比较:用equals方法!!! dlm.addElement(line); try { line = in.readLine(); } catch (IOException e1) { e1.printStackTrace(); } } return dlm; } @Override protected void done() { System.out.println(getState()); }} 楼上的高亮被代码块escape了高亮部分违反了EDT规则@SuppressWarnings("rawtypes")public class UpdateUsersWorker extends SwingWorker<DefaultListModel, String> { private BufferedReader in; private PrintWriter out; private DefaultListModel dlm; public UpdateUsersWorker(BufferedReader in, PrintWriter out, DefaultListModel dlm) { this.in = in; this.out = out; this.dlm = dlm; } @SuppressWarnings("unchecked") @Override protected DefaultListModel doInBackground() throws Exception { System.out.println(getState()); out.println("12");//向服务器发送12号功能请求 dlm.clear();//清空原有数据 String line = null; try { line = in.readLine(); } catch (IOException e1) { e1.printStackTrace(); }//用一个临时变量存储readLine的返回值 while ((!line.equals("END")) && (line != null)) {//String的比较:用equals方法!!! dlm.addElement(line); try { line = in.readLine(); } catch (IOException e1) { e1.printStackTrace(); } } return dlm; } @Override protected void done() { System.out.println(getState()); }} 只对dlm操作也不行吗?我原本以为,没有直接操作UI组件都是可以的。是不是因为dlm会随时触发相应的JList的改变呢?所以相当于是直接在worker thread上操作了UI组件……对嘛?谢谢! 您的意思是不是说,这些个线程在CPU中切换时间片引起的?我还不是太明白哈,麻烦您再说说,谢谢! 是的。DefaultListModel 内部使用了 Vector,但是这不足以保证它就是“线程安全”的,也不能保证你就可以安全的 “在非EDT线程里调用它的方法,改变它的内部状态”。Swing 控件的 UI 类对其 model 的查询访问可能是任何形式,可能在不加任何保护的情况下多次调用某些方法,因为 UI 类首先假设其 model 是单线程环境访问的,就是说它假设当它调用 model 的时候,同一时间它是唯一的调用者,如果有任何其他的代码会对model做出更改,那些代码也是在过去或者将来的某个时间在同一个线程(EDT)里运行的。所以,这里要确保 model 的线程安全几乎只剩下一种选择: 只在EDT内访问model。你这个例子应该是 BasicListUI 在同一个方法内两次调用 model.getSize() 得到的结果不一致造成的。 实际上我的回答的意思和楼上是一样的,你在非EDT中操作了组件,更改了组件的状态。更具体点来说就是考虑这个过程,EDT在用dlm的数据渲染组件时候,你定义的UpdateUsersWorker获得执行机会,删除了里面的数据,但是还没有再次加载dlm的数据的时候,EDT线程重新得到执行,继续用dlm渲染组件,因此就出现了错误,但是这个是很偶然才会出现。 按照您说的思路,我修改了代码如下:package gui_sockect_weibo_client;import java.io.BufferedReader;import java.io.IOException;import java.io.PrintWriter;import java.util.ArrayList;import java.util.Arrays;import java.util.List;import java.util.concurrent.ExecutionException;import javax.swing.DefaultListModel;import javax.swing.JList;import javax.swing.SwingUtilities;import javax.swing.SwingWorker;@SuppressWarnings("rawtypes")public class UpdateUsersWorker extends SwingWorker<DefaultListModel, String> { private BufferedReader in; private PrintWriter out; private DefaultListModel dlm; private JList usersList; public UpdateUsersWorker(BufferedReader in, PrintWriter out, DefaultListModel dlm, JList usersList) { this.in = in; this.out = out; this.dlm = dlm; this.usersList = usersList; } @SuppressWarnings("unchecked") @Override protected DefaultListModel doInBackground() throws Exception { System.out.println(getState()); out.println("12");//向服务器发送12号功能请求// SwingUtilities.invokeLater(new Runnable() {//EDT!!! // @Override// public void run() {// dlm.clear();//清空原有数据 // }// }); String line = null;//用一个临时变量存储readLine的返回值 try { line = in.readLine(); } catch (IOException e1) { e1.printStackTrace(); } ArrayList<String> toPublishList = new ArrayList<String>(); while ((!line.equals("END")) && (line != null)) {//String的比较:用equals方法!!!// dlm.addElement(line);// publish(line); toPublishList.add(line); try { line = in.readLine(); } catch (IOException e1) { e1.printStackTrace(); } } String[] toPublishArray = new String[toPublishList.size()]; toPublishList.toArray(toPublishArray); publish(toPublishArray); return dlm; } @SuppressWarnings("unchecked") @Override protected void process(List<String> chunks) { dlm.clear();//清空原有数据 for (String string : chunks) { dlm.addElement(string); } } @Override protected void done() { System.out.println(getState()); usersList.repaint(); usersList.revalidate(); usersList.updateUI(); }}请问:1、现在我再去调试,暂时没有发现异常和界面更新产生的空白现象。但是我不知道是否还会出现原来的异常和空白,我觉得代码这次应该没问题了,您觉得呢?2、这个问题与这篇帖子http://bbs.csdn.net/topics/80273103有关系吗?谢谢! 不好意思,代码块内部的设置没起作用。主要修改之处是:加入了process方法。您看看,好像没问题了吧?因为process方法是在EDT上调用的,是吗?谢谢! process 方法我没有用过,我一般直接用 SwingUtilities.invokeLater()。JList 没有必要去手动调用 repaint() revalidate() 和 updateUI()revalidate() 是告诉 Swing 你的一个 Container 内部的 layout 有变化repaint() 送出异步重绘请求(到EDT中排队)updateUI() 应该会导致UI类的更新你在调用 model 的 addElement 方法时,它已经fire event,并导致 JList 重绘了。代码仅供参考:updateUsersButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { dlm.clear(); new UpdateUsersWorker(in, out, dlm).execute(); }});@SuppressWarnings("rawtypes")public class UpdateUsersWorker extends SwingWorker<DefaultListModel, String> { private BufferedReader in; private PrintWriter out; private DefaultListModel dlm; public UpdateUsersWorker(BufferedReader in, PrintWriter out, DefaultListModel dlm) { this.in = in; this.out = out; this.dlm = dlm; } @SuppressWarnings("unchecked") @Override protected DefaultListModel doInBackground() throws Exception { System.out.println(getState()); out.println("12");//向服务器发送12号功能请求 //dlm.clear();//清空原有数据 String line = null; try { line = in.readLine(); } catch (IOException e1) { e1.printStackTrace(); }//用一个临时变量存储readLine的返回值 while ((!line.equals("END")) && (line != null)) {//String的比较:用equals方法!!! //dlm.addElement(line); addLine(line); try { line = in.readLine(); } catch (IOException e1) { e1.printStackTrace(); } } return dlm; } private void addLine(final String line) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { dlm.addElement(line); } }); } @Override protected void done() { System.out.println(getState()); }} android<Intent-filter>配置的问题 java反射 获取类里的一个静态类 计算一个字符串中有多少个相同子串个数的方法 syso里怎么不调用toString()啊? 咨询一下搜索引擎具体实现? 关于JAVA插件Visual Editor的问题? 在线等待,请教关于参数传递的问题!! "无效的游标状态"是什么意思?怎么解决? java 图表用什么 快速排序选最右侧的元素做主元 java扫描局域网所有活动主机 关于使用java做一个模拟聊天程序qq的疑问?
点击10次当然是new十个啦。你在事件dispatch线程中刷新UI,最好使用SwingUtilties.invokelater
针对您刚刚的回复,我的问题是:
1、我原先也试过改成只new一个UpdateUserWorker实例,然后调用该实例的execute方法,结果还是开了10个线程。那这个怎么解释呢?
2、“在事件dispatch线程中刷新UI,最好使用SwingUtilties.invokelater ”这个道理我明白,您是说我哪里没有遵循这个规则了?
3、是不是有时候出现的空白,和上图中的异常,和上述2中的问题相关,是应该在EDT上更新GUI组件引起的吗?
谢谢!!!
而报错和空白可能是由UpdateUsersWorker updateUsersWorker = new UpdateUsersWorker(in, out, dlm);中的dlm引起,当EDT在渲染list时候,SwingWorker结果CPU时间片,然后清空dlm ,然后EDT接管CPU时间片,继续渲染时候,dlm里面已经没有数据,因此报错。
public class UpdateUsersWorker extends SwingWorker<DefaultListModel, String> {
private BufferedReader in;
private PrintWriter out;
private DefaultListModel dlm;
public UpdateUsersWorker(BufferedReader in, PrintWriter out, DefaultListModel dlm) {
this.in = in;
this.out = out;
this.dlm = dlm;
}
@SuppressWarnings("unchecked")
@Override
protected DefaultListModel doInBackground() throws Exception {
System.out.println(getState());
out.println("12");//向服务器发送12号功能请求
dlm.clear();//清空原有数据
String line = null;
try {
line = in.readLine();
} catch (IOException e1) {
e1.printStackTrace();
}//用一个临时变量存储readLine的返回值
while ((!line.equals("END")) && (line != null)) {//String的比较:用equals方法!!!
dlm.addElement(line);
try {
line = in.readLine();
} catch (IOException e1) {
e1.printStackTrace();
}
}
return dlm;
}
@Override
protected void done() {
System.out.println(getState());
}
}
高亮部分违反了EDT规则@SuppressWarnings("rawtypes")
public class UpdateUsersWorker extends SwingWorker<DefaultListModel, String> {
private BufferedReader in;
private PrintWriter out;
private DefaultListModel dlm;
public UpdateUsersWorker(BufferedReader in, PrintWriter out, DefaultListModel dlm) {
this.in = in;
this.out = out;
this.dlm = dlm;
}
@SuppressWarnings("unchecked")
@Override
protected DefaultListModel doInBackground() throws Exception {
System.out.println(getState());
out.println("12");//向服务器发送12号功能请求
dlm.clear();//清空原有数据
String line = null;
try {
line = in.readLine();
} catch (IOException e1) {
e1.printStackTrace();
}//用一个临时变量存储readLine的返回值
while ((!line.equals("END")) && (line != null)) {//String的比较:用equals方法!!!
dlm.addElement(line);
try {
line = in.readLine();
} catch (IOException e1) {
e1.printStackTrace();
}
}
return dlm;
}
@Override
protected void done() {
System.out.println(getState());
}
}
谢谢!
我还不是太明白哈,麻烦您再说说,谢谢!
是的。DefaultListModel 内部使用了 Vector,但是这不足以保证它就是“线程安全”的,也不能保证你就可以安全的 “在非EDT线程里调用它的方法,改变它的内部状态”。Swing 控件的 UI 类对其 model 的查询访问可能是任何形式,可能在不加任何保护的情况下多次调用某些方法,因为 UI 类首先假设其 model 是单线程环境访问的,就是说它假设当它调用 model 的时候,同一时间它是唯一的调用者,如果有任何其他的代码会对model做出更改,那些代码也是在过去或者将来的某个时间在同一个线程(EDT)里运行的。所以,这里要确保 model 的线程安全几乎只剩下一种选择: 只在EDT内访问model。你这个例子应该是 BasicListUI 在同一个方法内两次调用 model.getSize() 得到的结果不一致造成的。
更具体点来说就是考虑这个过程,EDT在用dlm的数据渲染组件时候,你定义的UpdateUsersWorker获得执行机会,删除了里面的数据,但是还没有再次加载dlm的数据的时候,EDT线程重新得到执行,继续用dlm渲染组件,因此就出现了错误,但是这个是很偶然才会出现。
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;@SuppressWarnings("rawtypes")
public class UpdateUsersWorker extends SwingWorker<DefaultListModel, String> {
private BufferedReader in;
private PrintWriter out;
private DefaultListModel dlm;
private JList usersList;
public UpdateUsersWorker(BufferedReader in, PrintWriter out, DefaultListModel dlm, JList usersList) {
this.in = in;
this.out = out;
this.dlm = dlm;
this.usersList = usersList;
}
@SuppressWarnings("unchecked")
@Override
protected DefaultListModel doInBackground() throws Exception {
System.out.println(getState());
out.println("12");//向服务器发送12号功能请求
// SwingUtilities.invokeLater(new Runnable() {//EDT!!!
// @Override
// public void run() {
// dlm.clear();//清空原有数据
// }
// });
String line = null;//用一个临时变量存储readLine的返回值
try {
line = in.readLine();
} catch (IOException e1) {
e1.printStackTrace();
}
ArrayList<String> toPublishList = new ArrayList<String>();
while ((!line.equals("END")) && (line != null)) {//String的比较:用equals方法!!!
// dlm.addElement(line);
// publish(line);
toPublishList.add(line);
try {
line = in.readLine();
} catch (IOException e1) {
e1.printStackTrace();
}
}
String[] toPublishArray = new String[toPublishList.size()];
toPublishList.toArray(toPublishArray);
publish(toPublishArray);
return dlm;
}
@SuppressWarnings("unchecked")
@Override
protected void process(List<String> chunks) {
dlm.clear();//清空原有数据
for (String string : chunks) {
dlm.addElement(string);
}
} @Override
protected void done() {
System.out.println(getState());
usersList.repaint();
usersList.revalidate();
usersList.updateUI();
}
}请问:
1、现在我再去调试,暂时没有发现异常和界面更新产生的空白现象。但是我不知道是否还会出现原来的异常和空白,我觉得代码这次应该没问题了,您觉得呢?
2、这个问题与这篇帖子http://bbs.csdn.net/topics/80273103有关系吗?谢谢!
主要修改之处是:加入了process方法。
您看看,好像没问题了吧?因为process方法是在EDT上调用的,是吗?
谢谢!
process 方法我没有用过,我一般直接用 SwingUtilities.invokeLater()。
JList 没有必要去手动调用 repaint() revalidate() 和 updateUI()revalidate() 是告诉 Swing 你的一个 Container 内部的 layout 有变化
repaint() 送出异步重绘请求(到EDT中排队)
updateUI() 应该会导致UI类的更新你在调用 model 的 addElement 方法时,它已经fire event,并导致 JList 重绘了。代码仅供参考:
updateUsersButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dlm.clear();
new UpdateUsersWorker(in, out, dlm).execute();
}
});@SuppressWarnings("rawtypes")
public class UpdateUsersWorker extends SwingWorker<DefaultListModel, String> {
private BufferedReader in;
private PrintWriter out;
private DefaultListModel dlm;
public UpdateUsersWorker(BufferedReader in, PrintWriter out, DefaultListModel dlm) {
this.in = in;
this.out = out;
this.dlm = dlm;
}
@SuppressWarnings("unchecked")
@Override
protected DefaultListModel doInBackground() throws Exception {
System.out.println(getState());
out.println("12");//向服务器发送12号功能请求 //dlm.clear();//清空原有数据 String line = null;
try {
line = in.readLine();
} catch (IOException e1) {
e1.printStackTrace();
}//用一个临时变量存储readLine的返回值
while ((!line.equals("END")) && (line != null)) {//String的比较:用equals方法!!! //dlm.addElement(line); addLine(line); try {
line = in.readLine();
} catch (IOException e1) {
e1.printStackTrace();
}
}
return dlm;
} private void addLine(final String line) {
SwingUtilities.invokeLater(new Runnable() { @Override
public void run() {
dlm.addElement(line);
}
});
}
@Override
protected void done() {
System.out.println(getState());
}
}