2013-06-29 2 views
5

스윙 JTable에는 많은 양의 데이터가 동적으로 업데이트됩니다. 새 행이 지속적으로 추가되고 몇 분 안에 1000-2000 행을 추가 할 수 있습니다. 일부 직원을 수행하기 위해 사용의 단일 행 선택 이벤트에 응답하기 위해 리스너를 등록했습니다. Swing 데이터 바인딩에 Observer 패턴을 사용했으며 테이블의 모델은 WritableList 구현에 의해 뒷받침됩니다. 따라서 새 항목이 자체 영역에서 테이블에 추가됩니다. 그리고 Listener가 SWT UI 스레드에서 추가되었습니다. 문제는 새로운 행이 테이블에 추가되면 사용자 행 선택 이벤트에서 한 번에 응답하지 않는다는 것입니다. 테이블 모델 업데이트를 중지 할 때만 테이블이 사용자 선택에 응답합니다 .- 몇 번 더 지연되고 30-60 초가 걸립니다. 집중적으로 업데이트 할 때 내 테이블 모델이 사용자 선택에 한 번에 응답하지 않는 이유와이 제한을 극복하는 방법을 알려주세요. 제안 사항을 보내 주시면 감사하겠습니다.거대한 데이터로드에서 JTable 행 선택 이벤트에 대한 지연된 응답

답변

11

SwingWorker ~ publish() 행을 백그라운드로 사용하고 process() 구현에서 TableModel을 업데이트하십시오. SwingWorker은 지속 가능한 속도로 업데이트됩니다. limitProfile을 사용하여 event dispatch thread을 차단하지 않도록하십시오.

부록 : GUI는이 1,000,000 행의 variation을 테스트 한대로 반응합니다. 프로파일 링 할 때 "이동"버튼을 클릭 할 때마다 새 작업자가 시작됩니다.

import java.awt.BorderLayout; 
import java.awt.Component; 
import java.awt.EventQueue; 
import java.awt.event.ActionEvent; 
import java.text.DateFormat; 
import java.text.SimpleDateFormat; 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.Date; 
import java.util.List; 
import javax.swing.AbstractAction; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JScrollPane; 
import javax.swing.JTable; 
import javax.swing.SwingWorker; 
import javax.swing.table.AbstractTableModel; 
import javax.swing.table.DefaultTableCellRenderer; 

/** @see https://stackoverflow.com/a/17415635/230513 */ 
public class TestTableLoad01 { 

    public static void main(String[] args) { 
     new TestTableLoad01(); 
    } 

    public TestTableLoad01() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       final MyTableModel model = new MyTableModel(); 
       JTable table = new JTable(model); 
       table.setDefaultRenderer(Date.class, new TimeCellRenderer()); 

       JFrame frame = new JFrame("Testing"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setLayout(new BorderLayout()); 
       frame.add(new JScrollPane(table)); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
       frame.add(new JButton(new AbstractAction("Go") { 
        @Override 
        public void actionPerformed(ActionEvent e) { 
         TableSwingWorker worker = new TableSwingWorker(model); 
         worker.execute(); 
        } 
       }), BorderLayout.SOUTH); 
      } 
     }); 
    } 

    public class TimeCellRenderer extends DefaultTableCellRenderer { 

     private DateFormat df; 

     public TimeCellRenderer() { 
      df = new SimpleDateFormat("HH:mm:ss"); 
     } 

     @Override 
     public Component getTableCellRendererComponent(JTable table, 
      Object value, boolean isSelected, boolean hasFocus, int row, int column) { 

      if (value instanceof Date) { 

       value = df.format(value); 

      } 

      super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 

      return this; 

     } 
    } 

    public class MyTableModel extends AbstractTableModel { 

     private String[] columnNames = new String[]{"Date", "Row"}; 
     private List<RowData> data; 

     public MyTableModel() { 
      data = new ArrayList<RowData>(25); 
     } 

     @Override 
     public Class<?> getColumnClass(int columnIndex) { 
      return columnIndex == 0 ? Date.class : Integer.class; 
     } 

     @Override 
     public String getColumnName(int col) { 
      return columnNames[col]; 
     } 

     @Override 
     public int getColumnCount() { 
      return columnNames.length; 
     } 

     @Override 
     public int getRowCount() { 
      return data.size(); 
     } 

     @Override 
     public Object getValueAt(int row, int col) { 
      RowData value = data.get(row); 
      return col == 0 ? value.getDate() : value.getRow(); 
     } 

     public void addRow(RowData value) { 
      int rowCount = getRowCount(); 
      data.add(value); 
      fireTableRowsInserted(rowCount, rowCount); 
     } 

     public void addRows(RowData... value) { 
      addRows(Arrays.asList(value)); 
     } 

     private void addRows(List<RowData> rows) { 
      int rowCount = getRowCount(); 
      data.addAll(rows); 
      fireTableRowsInserted(rowCount, getRowCount() - 1); 
     } 
    } 

    public class RowData { 

     private Date date; 
     private int row; 

     public RowData(int row) { 
      this.date = new Date(); 
      this.row = row; 
     } 

     public Date getDate() { 
      return date; 
     } 

     public int getRow() { 
      return row; 
     } 
    } 

    public class TableSwingWorker extends SwingWorker<MyTableModel, RowData> { 

     private final MyTableModel tableModel; 

     public TableSwingWorker(MyTableModel tableModel) { 
      this.tableModel = tableModel; 
     } 

     @Override 
     protected MyTableModel doInBackground() throws Exception { 

      // This is a deliberate pause to allow the UI time to render 
      Thread.sleep(1000); 

      System.out.println("Start polulating"); 

      for (int index = 0; index < 1000000; index++) { 

       RowData data = new RowData(index); 
       publish(data); 
       Thread.yield(); 
      } 
      return tableModel; 
     } 

     @Override 
     protected void process(List<RowData> chunks) { 
      System.out.println("Adding " + chunks.size() + " rows"); 
      tableModel.addRows(chunks); 
     } 
    } 
} 
+0

귀하의 회신에 감사드립니다, Trashgod. 나는 VisualVM을 사용하고 있었지만 이벤트 디스패치 스레드가 차단되었는지 여부에 대한 많은 정보를 얻지 못했습니다. 이 프로파일 러에서 활성 스레드 목록 만 볼 수 있습니다. 일부 스레드가 다른 스레드에 의해 차단되었는지 여부를 확인하는 방법? 미리 감사드립니다. – kioria

+0

'jvisualvm'에서 정확한 색 구성표가 기억 나지 않습니다. NetBeans에서는 빨간색입니다. [스레드 덤프] (http://javatechniques.com/blog/jdb-example-generating-a-thread-dump/)와 비교하십시오. [example] (http://stackoverflow.com/q/12728589/230513)). – trashgod

+0

새로운 행을 많이 게시하기 위해 생성하고 실행해야하는 스레드의 양에 대해 궁금합니다 ... 스레드 생성과 해체는 칩 작업이 아닙니다. 그리고 SwingWorker 프레임 워크를 사용하면 메소드 doInBackground()를 호출하기 위해 매번 새로운 인스턴스를 생성해야 할 필요가있는 것으로 보입니다. 그것은 효율적입니까? – kioria