2013-06-29 8 views
5

Mam Swing JTable dynamicznie aktualizowaną z dużą ilością danych - nowe wiersze są stale dodawane na żywo, a niektóre 1000-2000 wierszy można dodać w ciągu kilku minut. Zarejestrowałem odbiorcę do odpowiedzi na jednoosobowe wydarzenie wyboru, aby wykonać część personelu. Użyłem wzoru Observer do wiązania danych Swing, a model tabeli jest wspierany przez implementację WritableList. Nowe pozycje dodawane są do tabeli z własnej dziedziny. Dodał też Listener z wątku interfejsu użytkownika SWT. Problem polega na tym, że po dodaniu nowych wierszy do tabeli nie reaguje od razu na zdarzenie wyboru wiersza użytkownika. Dopiero po zatrzymaniu aktualizacji modelu tabeli tabela zareaguje na wybór użytkownika - czasami z opóźnieniem ponad 30-60 sekund. Proszę, pomóż mi podkreślić, dlaczego mój model tabeli nie reaguje natychmiast na wybór użytkownika podczas intensywnej aktualizacji i jak przezwyciężyć to ograniczenie. Wszelkie sugestie zostaną docenione.Opóźniona reakcja na zdarzenie selekcji wierszy JTable pod ogromnym obciążeniem danych

Odpowiedz

11

Zastosowanie SwingWorker do publish() twoje wiersze w tle i aktualizuje TableModel w swojej realizacji process(). SwingWorker będzie limit aktualizacje do stałej stopy. Profile, aby upewnić się, że nie blokujesz urządzenia event dispatch thread.

Dodatek: GUI reaguje zgodnie z testem 1 000 000 wierszy variation. Podczas profilowania pamiętaj, że każde kliknięcie przycisku "Idź" uruchamia nowego pracownika.

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

Dziękuję za odpowiedź, Trashgod. Używałem VisualVM - ale nie mam zbyt wiele informacji, czy wątek wysyłki zdarzenia został zablokowany, czy nie. Z tego profilera widzę tylko listę aktywnych wątków. Jak sprawdzić, czy wątek jest blokowany przez inny? Z góry dziękuję – kioria

+0

Nie pamiętam dokładnego schematu kolorów w 'jvisualvm'; w NetBeans jest czerwony; porównać z [zrzutem wątku] (http://javatechniques.com/blog/jdb-example-generating-a-thread-dump/), na [przykład] (http://stackoverflow.com/q/12728589/230513). – trashgod

+0

Zastanawiam się, ile wątków trzeba utworzyć i uruchomić, aby opublikować wiele nowych wierszy ... Wiem, że tworzenie i usuwanie wątków nie jest operacją chipową. I w przypadku SwingWorker wydaje mi się, że potrzebuję za każdym razem tworzyć nową instancję w celu wywołania metody doInBackground(). Czy to jest wydajne? – kioria

Powiązane problemy