如何从另一个线程更新JList?

时间:2014-01-06 05:07:50

标签: java multithreading swing jlist event-dispatch-thread

public class ListExample {
    public static void main(String[] args) {
       final List l=new List();
        l.init();
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                l.list.updateUI();
                System.out.println("Asim");
            }
        });
    }
}

public class List extends  JFrame{
    public DefaultListModel DLM=new DefaultListModel();
    public JList list=new JList(DLM);
    private JPanel jp=new JPanel();
    private JScrollPane sp=new JScrollPane(list);
    public void init(){
        jp.add(sp);
        super.add(jp);
        super.setVisible(true);
        super.setLayout(new FlowLayout());
        super.setSize(400,500);
        UL p=new UL();
        p.UL_1();
    }
    public void Update_list(String[] n){
         list.setListData(n);
        list.updateUI();
    }

}
public class UL extends  List{
    public void UL_1(){
       t.start();
    }
    Thread t=new Thread(new Runnable() {
        @Override
        public void run() {
            //To change body of implemented methods use File | Settings | File Templates.
            String[] n={"Asim", "saif","Khan"};
            List l=new List();
            l.list.setListData(n);
            list.updateUI();
        }
    });
}

2 个答案:

答案 0 :(得分:4)

Swing是一个单线程框架,这意味着您只需要在Event Dispatching Thread的上下文中修改UI元素。同样,任何长期任务或其他阻止过程都会阻止EDT处理新事件。

虽然有很多方法可以实现这一点,但最简单的方法可能是使用SwingWorker

例如......

请查看Concurrency in Swing了解详情

答案 1 :(得分:3)

你最好不要用那种糟糕的方式编写你的java代码。

不要让xxxList扩展JFrame,这会引起误解。 要使用线程更新列表,可以启动一个新线程,如下所示:

之前:不鼓励使用其他非EDT帖子,导致错误。

import java.awt.FlowLayout;
import javax.swing.*;
/*
 * This code is bad dealing with Swing component update
 * 
 * Update a Swing component from A Non-EDT thread is not encouraged
 * may  incur error like:
 * 
 * Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
 */
public class ListExampleBad {
    public static void main(String[] args) {
       final ListFrame listFrame=new ListFrame();
        listFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                //use a thread to update list data
                new Thread(new UpdateList(listFrame)).start();
                System.out.println("Asim");
            }
        });
    }
}
//list fram with JList
class ListFrame extends  JFrame{
    public ListFrame(){
        jp.add(sp);
        super.add(jp);
        super.setVisible(true);
        super.setLayout(new FlowLayout());
        super.setSize(400,500);
    }
    public synchronized void updateList(String[] n){
         list.setListData(n);
         list.updateUI();
    }
    private static final long serialVersionUID = 1L;
    private DefaultListModel<String> DLM=new DefaultListModel<String>();
    private JList<String> list=new JList<String>(DLM);
    private JPanel jp=new JPanel();
    private JScrollPane sp=new JScrollPane(list);
}
//runnable dealing with data update
class UpdateList implements Runnable {
    public UpdateList(ListFrame listFrame) {
        this.ListFrame = listFrame;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        if(SwingUtilities.isEventDispatchThread()) {
            System.out.println("updating list from Event Dispatch Thread");
        }else {
            System.out.println("updating list NOT from Event Dispatch Thread");
        }
         String[] n={"Asim", "saif","Khan"};
         ListFrame.updateList(n);
    }
    private ListFrame ListFrame;
}

感谢您发表评论的人,我更新了代码:

“核心java”指导我们的原则:

1)不要在EDT线程中使用SwingWorker做一个耗时的工作。

2)除了EDT之外,不要在其他线程中操作Swing组件。

此外,您可以使用SwingUtilities.isEventDispatchThread()来检查作业是否在EDT线程中进行。

单向:使用SwingUtilities.invokeLater

public class ListExampleBetter {
    public static void main(String[] args) {
       final ListFrameBad listFrame=new ListFrameBad();
       listFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       //use SwingUtilities.invokeLater,it's ok
       SwingUtilities.invokeLater(new UpdateList(listFrame));
    }
}

另一种方式:使用SwingWoker

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

import javax.swing.*;
/**
 * This example illustrate updating JList from thread
 */
public class ListExample {
    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                ListFrame listFrame = new ListFrame();
            listFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                listFrame.setLocationRelativeTo(null);
                listFrame.setVisible(true);
            }
        });
    }
}
//frame with JList
class ListFrame extends  JFrame{
    public ListFrame(){
        super("Update JList Demo");

        //initalize data field
        dataToUpdate =new String[]{"Asim", "saif","Khan"};
        DefaultListModel<String> DLM =new DefaultListModel<String>();
        DLM.addElement("wait for update...");;
        list =new JList<String>(DLM);

         //build gui
        JPanel btnPanel = new JPanel();
        JButton btnUpdate = new JButton("Update");
        btnPanel.add(btnUpdate);
        JScrollPane sp=new JScrollPane(list);
        this.add(btnPanel,BorderLayout.NORTH);
        this.add(sp,BorderLayout.CENTER);
        this.setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT);

        //deal with action
        btnUpdate.addActionListener(new ActionListener(){

            @Override
            public void actionPerformed(ActionEvent e) {
                // TODO Auto-generated method stub
                ListUpdater updater = new ListUpdater();
                updater.execute();
            }
        });
    }
    public synchronized void updateList(String[] n){
         list.setListData(n);
         list.updateUI();
    }
    //using Swingworker to update list
    private class ListUpdater extends SwingWorker<Void,String>{
        @Override
        public Void doInBackground() {
            for(String str :dataToUpdate ) {
                publish(str);
            }
            return null;
        }
        @Override
        public void process (List<String> datas) {
            for(String str : datas) {
                model.addElement(str);
            }
        }
        @Override
        public void done() {
            if(SwingUtilities.isEventDispatchThread()) {
                System.out.println("updating list from Event Dispatch Thread");
            }else {
                System.out.println("updating list NOT from Event Dispatch Thread");
            }
            list.setModel(model);
        }
        private DefaultListModel<String> model =new DefaultListModel<String>();
    }
    public String[] getDataToUpdate() {
        return dataToUpdate;
    }
    public void setDataToUpdate(String[] dataToUpdate) {
        this.dataToUpdate = dataToUpdate;
    }

    private static final long serialVersionUID = 1L;
    private final int DEFAULT_WIDTH = 300,DEFAULT_HEIGHT = 300;
    private JList<String> list ;
    private String[] dataToUpdate ;
}