Java Swing - Implementing Throttling for JTable [Updated: May 12, 2018, Created: May 11, 2018] |
|
||
Throttling is the process to control the rate at which data is received. In a Swing application, high frequency rates of data updates may cause painting events to be fired one after another, which may freeze the UI . We just need to avoid blocking the EDT (event dispatch thread) for a long time. In this example we will create a general purpose class to provide throttling. We will then apply throttling to JTable data updates. Examplepublic class Throttler { private Timer timer; public Throttler(int rate, Runnable updater) { timer = new Timer(rate, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { //this is already in EDT timer.stop(); updater.run(); } }); } public void updateReceived() { //timer must be accessed by EDT because actionPerformed is also invoked in EDT //otherwise we have to synchronized it. Not doing so might miss the events if (!SwingUtilities.isEventDispatchThread()) { throw new IllegalArgumentException("updateReceived() must be called in EDT"); } if (!timer.isRunning()) { timer.restart(); } } } Implementing Throttling to JTableHigh frequency Data Servicepublic enum TestDataService { Instance; private String[] currencyPairs = {"EUR/USD", "USD/JPY", "GBP/USD", "USD/CHF", "USD/CAD", "AUD/USD", "NZD/USD", "EUR/GBP", "EUR/AUD", "GBP/JPY", "CHF/JPY", "NZD/JPY", "GBP/CAD"}; public void listenToDataUpdate(BiConsumer<String, BigDecimal> forexListener) { ExecutorService es = Executors.newSingleThreadExecutor(); es.execute(new Runnable() { @Override public void run() { while (true) { int i = ThreadLocalRandom.current().nextInt(0, currencyPairs.length); BigDecimal rate = BigDecimal.valueOf(Math.random() * 10); rate = rate.setScale(2, RoundingMode.CEILING); forexListener.accept(currencyPairs[i], rate); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } }); } } Table Modelpublic class ForexTableModel extends AbstractTableModel { private Map<String, BigDecimal> forexRates = new TreeMap<>(); private String[] columnNames = {"Currency Pair", "Rate"}; public void updateRateWithoutFiringEvent(String currency, BigDecimal rate) { forexRates.put(currency, rate); } @Override public String getColumnName(int column) { return columnNames[column]; } @Override public int getRowCount() { return forexRates.size(); } @Override public int getColumnCount() { return columnNames.length; } @Override public Object getValueAt(int rowIndex, int columnIndex) { if (columnIndex == 0) { String currency = getCurrencyByRow(rowIndex); return currency; } else if (columnIndex == 1) { String currency = getCurrencyByRow(rowIndex); return forexRates.get(currency); } return null; } private String getCurrencyByRow(int rowIndex) { return forexRates.keySet() .toArray(new String[forexRates.size()])[rowIndex]; } } The main classWith throttling:public class ThrottlingExampleMain { public static void main(String[] args) { ForexTableModel tableModel = new ForexTableModel(); JTable table = new JTable(tableModel); listenToForexChanges(tableModel); JFrame frame = createFrame(); frame.add(new JScrollPane(table)); frame.setLocationRelativeTo(null); frame.setVisible(true); } private static void listenToForexChanges(ForexTableModel tableModel) { Throttler throttler = new Throttler(1000, new Runnable() { @Override public void run() { //it is in EDT tableModel.fireTableDataChanged(); } }); TestDataService.Instance.listenToDataUpdate( (pair, rate) -> SwingUtilities.invokeLater(() -> { //table model must be accessed in EDT for thread safety tableModel.updateRateWithoutFiringEvent(pair, rate); throttler.updateReceived(); })); } private static JFrame createFrame() { JFrame frame = new JFrame("Throttling Example"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(new Dimension(500, 300)); return frame; } } Without throttling:public class WithoutThrottlingExample { public static void main(String[] args) { ForexTableModel tableModel = new ForexTableModel(); JTable table = new JTable(tableModel); listenToForexChanges(tableModel); JFrame frame = createFrame(); frame.add(new JScrollPane(table)); frame.setLocationRelativeTo(null); frame.setVisible(true); } private static void listenToForexChanges(ForexTableModel tableModel) { TestDataService.Instance.listenToDataUpdate((pair, rate) -> { tableModel.updateRateWithoutFiringEvent(pair, rate); tableModel.fireTableDataChanged(); }); } private static JFrame createFrame() { JFrame frame = new JFrame("Without Throttling Example"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(new Dimension(500, 300)); return frame; } } ![]() For a large number of rows, the screen will freeze without throttling. Note that instead of using a class like Throttler, we can use a timer directly but the disadvantage of that approach will be that the timer will always be running even there's no updates. Example ProjectDependencies and Technologies Used:
|
|
||
|
|||
|