本帖最后由 frr0717 于 2012-12-20 16:31:40 编辑

解决方案 »

  1.   

    因为你按钮点击一次就会new一个swingwork thread
    点击10次当然是new十个啦。你在事件dispatch线程中刷新UI,最好使用SwingUtilties.invokelater
      

  2.   

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

  3.   

    SwingWorker 类似往EDT中插入一个任务,因此按一次就新建一个线程。
    而报错和空白可能是由UpdateUsersWorker updateUsersWorker = new UpdateUsersWorker(in, out, dlm);中的dlm引起,当EDT在渲染list时候,SwingWorker结果CPU时间片,然后清空dlm ,然后EDT接管CPU时间片,继续渲染时候,dlm里面已经没有数据,因此报错。
      

  4.   

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

  5.   

    楼上的高亮被代码块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());
             
        }
    }
      

  6.   

    只对dlm操作也不行吗?我原本以为,没有直接操作UI组件都是可以的。是不是因为dlm会随时触发相应的JList的改变呢?所以相当于是直接在worker thread上操作了UI组件……对嘛?
    谢谢!
      

  7.   

    您的意思是不是说,这些个线程在CPU中切换时间片引起的?
    我还不是太明白哈,麻烦您再说说,谢谢!
      

  8.   


    是的。DefaultListModel 内部使用了 Vector,但是这不足以保证它就是“线程安全”的,也不能保证你就可以安全的 “在非EDT线程里调用它的方法,改变它的内部状态”。Swing 控件的 UI 类对其 model 的查询访问可能是任何形式,可能在不加任何保护的情况下多次调用某些方法,因为 UI 类首先假设其 model 是单线程环境访问的,就是说它假设当它调用 model 的时候,同一时间它是唯一的调用者,如果有任何其他的代码会对model做出更改,那些代码也是在过去或者将来的某个时间在同一个线程(EDT)里运行的。所以,这里要确保 model 的线程安全几乎只剩下一种选择: 只在EDT内访问model。你这个例子应该是 BasicListUI 在同一个方法内两次调用 model.getSize() 得到的结果不一致造成的。
      

  9.   

    实际上我的回答的意思和楼上是一样的,你在非EDT中操作了组件,更改了组件的状态。
    更具体点来说就是考虑这个过程,EDT在用dlm的数据渲染组件时候,你定义的UpdateUsersWorker获得执行机会,删除了里面的数据,但是还没有再次加载dlm的数据的时候,EDT线程重新得到执行,继续用dlm渲染组件,因此就出现了错误,但是这个是很偶然才会出现。
      

  10.   

    按照您说的思路,我修改了代码如下: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有关系吗?
    谢谢!
      

  11.   

    不好意思,代码块内部的设置没起作用。
    主要修改之处是:加入了process方法。
    您看看,好像没问题了吧?因为process方法是在EDT上调用的,是吗?
    谢谢!
      

  12.   


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