SwingWorker相关问题!!!谢谢各位~

frr0717 2012-12-20 04:31:05
最近在使用SwingWorker,是初学,希望各位指教!
我的代码片段如下:
updateUsersButton.addActionListener(new ActionListener() {			
@Override
public void actionPerformed(ActionEvent e) {

UpdateUsersWorker updateUsersWorker = new UpdateUsersWorker(in, out, dlm);
updateUsersWorker.execute();
}
});

以上代码是想实现每当点击updateUsersButton时,更新所有注册用户。具体的SwingWorker实现如下:
@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());

}
}

问题:
1、文档上面说:“SwingWorker 被设计为只执行一次。多次执行 SwingWorker 将不会调用两次 doInBackground 方法。”可是,我调试的时候,不断点击那个更新按钮,发现最多有10个SwingWorker的后台线程,如下图:

并且,我每单击一次更新按钮,doInBackground方法会打印出SwingWorker的状态,那为什么是只执行一次呢?

2、有时候更新界面没有问题;可是有时候更新界面之后,会使界面出现完全空白的样子,如下图:

但是,有时候又可以是正常刷新的,如下图:

此外,还会时不时出现异常,都不知道为什么:


急待各位解答,谢谢!!
...全文
383 14 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
raistlic 2012-12-28
  • 打赏
  • 举报
回复
引用 12 楼 frr0717 的回复:
引用 11 楼 frr0717 的回复:引用 9 楼 raistlic 的回复:引用 7 楼 frr0717 的回复:引用 6 楼 raistlic 的回复:楼上的高亮被代码块escape了 高亮部分违反了EDT规则 @SuppressWarnings("rawtypes") public class UpdateUsersWorker extends Swin……
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());
    }
}
frr0717 2012-12-27
  • 打赏
  • 举报
回复
引用 4 楼 huangjunnan 的回复:
SwingWorker 类似往EDT中插入一个任务,因此按一次就新建一个线程。 而报错和空白可能是由UpdateUsersWorker updateUsersWorker = new UpdateUsersWorker(in, out, dlm);中的dlm引起,当EDT在渲染list时候,SwingWorker结果CPU时间片,然后清空dlm ,然后EDT接管CPU时……
您的意思是不是说,这些个线程在CPU中切换时间片引起的? 我还不是太明白哈,麻烦您再说说,谢谢!
frr0717 2012-12-27
  • 打赏
  • 举报
回复
引用 6 楼 raistlic 的回复:
楼上的高亮被代码块escape了 高亮部分违反了EDT规则 @SuppressWarnings("rawtypes") public class UpdateUsersWorker extends SwingWorker<DefaultListModel, String> { private BufferedReader in; privat……
只对dlm操作也不行吗?我原本以为,没有直接操作UI组件都是可以的。是不是因为dlm会随时触发相应的JList的改变呢?所以相当于是直接在worker thread上操作了UI组件……对嘛? 谢谢!
raistlic 2012-12-27
  • 打赏
  • 举报
回复
楼上的高亮被代码块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()); } }
raistlic 2012-12-27
  • 打赏
  • 举报
回复
1L +1
引用 2 楼 frr0717 的回复:
2、“在事件dispatch线程中刷新UI,最好使用SwingUtilties.invokelater ”这个道理我明白,您是说我哪里没有遵循这个规则了?

@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());
         
    }
}
frr0717 2012-12-27
  • 打赏
  • 举报
回复
引用 11 楼 frr0717 的回复:
引用 9 楼 raistlic 的回复:引用 7 楼 frr0717 的回复:引用 6 楼 raistlic 的回复:楼上的高亮被代码块escape了 高亮部分违反了EDT规则 @SuppressWarnings("rawtypes") public class UpdateUsersWorker extends SwingWorker<DefaultListM……
不好意思,代码块内部的设置没起作用。 主要修改之处是:加入了process方法。 您看看,好像没问题了吧?因为process方法是在EDT上调用的,是吗? 谢谢!
frr0717 2012-12-27
  • 打赏
  • 举报
回复
引用 9 楼 raistlic 的回复:
引用 7 楼 frr0717 的回复:引用 6 楼 raistlic 的回复:楼上的高亮被代码块escape了 高亮部分违反了EDT规则 @SuppressWarnings("rawtypes") public class UpdateUsersWorker extends SwingWorker<DefaultListModel, String> { ……
按照您说的思路,我修改了代码如下:

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有关系吗?谢谢!
Hjn1861 2012-12-27
  • 打赏
  • 举报
回复
实际上我的回答的意思和楼上是一样的,你在非EDT中操作了组件,更改了组件的状态。 更具体点来说就是考虑这个过程,EDT在用dlm的数据渲染组件时候,你定义的UpdateUsersWorker获得执行机会,删除了里面的数据,但是还没有再次加载dlm的数据的时候,EDT线程重新得到执行,继续用dlm渲染组件,因此就出现了错误,但是这个是很偶然才会出现。
引用 8 楼 frr0717 的回复:
引用 4 楼 huangjunnan 的回复:SwingWorker 类似往EDT中插入一个任务,因此按一次就新建一个线程。 而报错和空白可能是由UpdateUsersWorker updateUsersWorker = new UpdateUsersWorker(in, out, dlm);中的dlm引起,当EDT在渲染list时候,SwingWorker结果CPU时……
raistlic 2012-12-27
  • 打赏
  • 举报
回复
引用 7 楼 frr0717 的回复:
引用 6 楼 raistlic 的回复:楼上的高亮被代码块escape了 高亮部分违反了EDT规则 @SuppressWarnings("rawtypes") public class UpdateUsersWorker extends SwingWorker<DefaultListModel, String> { private BufferedRe……
是的。 DefaultListModel 内部使用了 Vector,但是这不足以保证它就是“线程安全”的,也不能保证你就可以安全的 “在非EDT线程里调用它的方法,改变它的内部状态”。 Swing 控件的 UI 类对其 model 的查询访问可能是任何形式,可能在不加任何保护的情况下多次调用某些方法,因为 UI 类首先假设其 model 是单线程环境访问的,就是说它假设当它调用 model 的时候,同一时间它是唯一的调用者,如果有任何其他的代码会对model做出更改,那些代码也是在过去或者将来的某个时间在同一个线程(EDT)里运行的。 所以,这里要确保 model 的线程安全几乎只剩下一种选择: 只在EDT内访问model。 你这个例子应该是 BasicListUI 在同一个方法内两次调用 model.getSize() 得到的结果不一致造成的。
Hjn1861 2012-12-26
  • 打赏
  • 举报
回复
SwingWorker 类似往EDT中插入一个任务,因此按一次就新建一个线程。 而报错和空白可能是由UpdateUsersWorker updateUsersWorker = new UpdateUsersWorker(in, out, dlm);中的dlm引起,当EDT在渲染list时候,SwingWorker结果CPU时间片,然后清空dlm ,然后EDT接管CPU时间片,继续渲染时候,dlm里面已经没有数据,因此报错。
frr0717 2012-12-20
  • 打赏
  • 举报
回复
自己更正一下,线程的那张图,红色方框圈错了,应该为下图所示:

谢谢!!!
frr0717 2012-12-20
  • 打赏
  • 举报
回复
引用 1 楼 jia20003 的回复:
因为你按钮点击一次就会new一个swingwork thread 点击10次当然是new十个啦。 你在事件dispatch线程中刷新UI,最好使用SwingUtilties.invokelater
大牛,首先谢谢您上次的解答!!! 针对您刚刚的回复,我的问题是: 1、我原先也试过改成只new一个UpdateUserWorker实例,然后调用该实例的execute方法,结果还是开了10个线程。那这个怎么解释呢? 2、“在事件dispatch线程中刷新UI,最好使用SwingUtilties.invokelater ”这个道理我明白,您是说我哪里没有遵循这个规则了? 3、是不是有时候出现的空白,和上图中的异常,和上述2中的问题相关,是应该在EDT上更新GUI组件引起的吗? 谢谢!!!
gloomyfish 2012-12-20
  • 打赏
  • 举报
回复
因为你按钮点击一次就会new一个swingwork thread 点击10次当然是new十个啦。 你在事件dispatch线程中刷新UI,最好使用SwingUtilties.invokelater

62,634

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧