java - Jfreechart索引越界异常

标签 java swing thread-safety jfreechart

这是我第一次在 StackOverFlow 上发帖,所以请原谅我的帖子中的任何错误。 说到这个主题,我有一个程序应该绘制某些 X 和 Y 坐标。 我目前正在使用 JFREECHART 进行 XYChart。 为了满足高速更新的要求我有2个系列。 Series1 积累了 1000 个数据点,然后添加到图表中进行显示。在该 series2 积累了 1000 个数据点之后,完成后,它会清除Series1(为新的 1000 个数据点做好准备)并将 Series2 附加到图表(现在有 1000 个数据点) )...这个循环继续下去。 问题在于 XYSeries.clear 和 XYSeries.add 函数。显然他们创建了自己的线程,在他们完成执行之前我已经生成了一个新线程,因为我的应用程序非常快。我认为这导致了 indexoutobounds 错误。我能够通过以下方式猜测这一点: http://www.jfree.org/phpBB2/viewtopic.php?p=68111 类似的错误。 解决方案似乎是: Swing Thread Safe Programming

这里的解决方案是使用 SwingUtilities.invokeLater。但我不明白如何在我的应用程序中使用它。 我的代码是:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import static java.lang.Thread.sleep;
import org.jfree.chart.axis.ValueAxis;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

public class dataPlotter {

    private static int x = 0;
    private static boolean thread_start = false;
    private static final double PI = 3.1428571;
    private static boolean thread_run = true;
    private static double voltage = 0;
    private static boolean useSeries1 = true;

    public static void main(String[] args) {

        //create and configure window
        JFrame window = new JFrame();
        window.setTitle("Data Plotter GUI");
        window.setSize(600, 400);
        window.setLayout(new BorderLayout());
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //create a drop down box and connect button, then place that at the top of the window 
        JButton connectButton = new JButton("Start");
        JPanel topPanel = new JPanel();
        topPanel.add(connectButton);
        window.add(topPanel, BorderLayout.NORTH);

        //create the line graph
        XYSeries series1 = new XYSeries("Voltage Characteristics");
        XYSeries series2 = new XYSeries("Voltage Characteristics");
        XYSeriesCollection dataset = new XYSeriesCollection(series1);
        JFreeChart chart = ChartFactory.createXYLineChart("Sine Wave", "Time", "Voltage", dataset);
        window.add(new ChartPanel(chart), BorderLayout.CENTER);

        //set range of x axis and range of series 
        XYPlot xyPlot = chart.getXYPlot();
        ValueAxis domainAxis = xyPlot.getDomainAxis();
        ValueAxis rangeAxis = xyPlot.getRangeAxis();
        rangeAxis.setRange(-1.5, 1.5);
        domainAxis.setRange(0, 1000);

        //Declaring thread
        Thread thread;
        thread = new Thread() {
            @Override
            public void run() {
                thread_start = true;
                int count_y = 0;
                thread_run = true;
                int count = 0;
                while (thread_run) {
                    //create "Y" datapoint i.e. voltage
                    if (count_y < 800) {
                        count_y++;
                    } else {
                        count_y = 0;
                    }
                    voltage = Math.sin(2 * PI * count_y / 800);

                    //add datapoint to CHART
                    try {
                        //use series 1 to accumulate 1000 datapoints
                        if(useSeries1){
                            synchronized (series1) {
                                if (series1.getItemCount() > 1000) {
                                    domainAxis.setRange(x - 1001, x);
                                }
                                series1.add(x++, voltage);
                                sleep(1);
                            }
                            synchronized (series1) {
                                window.repaint();
                            }

                            synchronized (series1) {
                                if (series1.getItemCount() ==1001) {

                                    dataset.removeAllSeries();
                                    series2.clear();
                                    domainAxis.setRange(0, 1000);
                                    dataset.addSeries(series1);
                                    useSeries1 = false;
                                    sleep(5);
                                    x = 0;
                                    window.repaint();
                                }
                            }
                        } else{
                            synchronized (series2) {
                                if (series2.getItemCount() > 1000) {
                                    domainAxis.setRange(x - 1001, x);
                                }
                                series2.add(x++, voltage);
                                sleep(1);
                            }
                            synchronized (series2) {
                                window.repaint();
                            }
                            synchronized (series2) {
                                if (series2.getItemCount() == 1001) {
                                    dataset.removeAllSeries();
                                    series1.clear();
                                    domainAxis.setRange(0, 1000);
                                    dataset.addSeries(series2);
                                    useSeries1 = true;
                                    sleep(5);
                                    x = 0;
                                    window.repaint();
                                 }
                            }
                        }
                    } catch (Exception er) { }
                }
                thread_start = false;
             }
            };

        //configure the connect button and use another thread to listen for data
        connectButton.addActionListener(new ActionListener() {
            @Override
            @SuppressWarnings("empty-statement")
            public void actionPerformed(ActionEvent e) {
                if (connectButton.getText().equals("Start")) {

                    connectButton.setText("Stop");

                    thread.start();
                } else {
                    //disconnect from the serial port
                    thread_run = false;

                    //while (thread_start);
                    connectButton.setText("Start");
                    //synchronized (series) {
                    //series.clear();
                    //}
                    //x = 0;

                }
            }

        });

        //Display winow
        window.setVisible(true);

    }
}

最佳答案

根据设计,Swing 不是线程安全的。您必须从除 Swing 事件泵线程之外的另一个线程使用 SwingUtilities.invokeLater( ... ),如所讨论的 here .

tutorial about Swing可能有帮助。

这是一个面向对象的提案。希望这会有所帮助。

public class Plotter implements Runnable {

   private static final String BTN_START = "Start";
   private static final String BTN_STOP  = "Stop";

   private final JFrame             window        = new JFrame();
   private final JButton            connectButton = new JButton( BTN_START );
   private final XYSeries           series1       = new XYSeries("Voltage Characteristics");
   private final XYSeries           series2       = new XYSeries("Voltage Characteristics");
   private final XYSeriesCollection dataset       = new XYSeriesCollection( series1 );
   private final JFreeChart         chart         =
      ChartFactory.createXYLineChart("Sine Wave", "Time", "Voltage", dataset);
   private final XYPlot             xyPlot        = chart.getXYPlot();
   private final ValueAxis          domainAxis    = xyPlot.getDomainAxis();
   private final ValueAxis          rangeAxis     = xyPlot.getRangeAxis();
   private /* */ Thread             thread        = null;
   private /* */ boolean            thread_run    = true;
   private /* */ double             voltage       = 0;
   private /* */ boolean            useSeries1    = true;
   private /* */ int                x             = 0;

   public Plotter() {
      window.setTitle("Data Plotter GUI");
      window.setSize(600, 400);
      window.setLayout(new BorderLayout());
      window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      final JPanel topPanel = new JPanel();
      topPanel.add(connectButton);
      window.add(topPanel, BorderLayout.NORTH);
      window.add(new ChartPanel(chart), BorderLayout.CENTER);
      rangeAxis.setRange(-1.5, 1.5);
      domainAxis.setRange(0, 1000);
      connectButton.addActionListener( e -> startOrStop());
      window.setVisible(true);
   }

   private void startOrStop() {
      if (connectButton.getText().equals( BTN_START )) {
         connectButton.setText( BTN_STOP );
         synchronized( series1 ) { series1.clear(); }
         synchronized( series2 ) { series2.clear(); }
         thread = new Thread( Plotter.this );
         thread.setName( "Plotter" );
         thread.setDaemon( true );
         thread.start();
      }
      else {
         thread_run = false;
         //disconnect from the serial port
         connectButton.setText( BTN_START );
         try {
            thread.join( 10_000L );
         }
         catch( final InterruptedException e ){
            e.printStackTrace();
         }
      }
   }

   private void updateSeries1() {
      if (series1.getItemCount() > 1000) {
         domainAxis.setRange(x - 1001, x);
      }
      series1.add( x++, voltage );
      window.repaint();
      if (series1.getItemCount() ==1001) {

         dataset.removeAllSeries();
         series2.clear();
         domainAxis.setRange(0, 1000);
         dataset.addSeries(series1);
         useSeries1 = false;
         window.repaint();
         x = 0;
      }
   }

   private void updateSeries2() {
      if (series2.getItemCount() > 1000) {
         domainAxis.setRange(x - 1001, x);
      }
      series2.add( x++, voltage );
      window.repaint();
      if( series2.getItemCount() == 1001 ) {
         dataset.removeAllSeries();
         series1.clear();
         domainAxis.setRange(0, 1000);
         dataset.addSeries(series2);
         useSeries1 = true;
         window.repaint();
         x = 0;
      }
   }

   @Override
   public void run() {
      x = 0;
      int count_y = 0;
      thread_run = true;
      while( thread_run ) {
         if (count_y < 800) {
            count_y++;
         }
         else {
            count_y = 0;
         }
         voltage = Math.sin(( 2 * Math.PI * count_y ) / 800);
         try {
            // Push a new job in the Swing queue
            if( useSeries1 ) {
               SwingUtilities.invokeLater(() -> updateSeries1());
            }
            else {
               SwingUtilities.invokeLater(() -> updateSeries2());
            }
         }
         catch (final Exception er) {
            er.printStackTrace();
         }
         try {
            Thread.sleep( 2L ); // Give time to Swing to execute the job
         }
         catch( final InterruptedException e ){
            e.printStackTrace();
         }
      }
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(() -> new Plotter());
   }
}

关于java - Jfreechart索引越界异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44992594/

相关文章:

java - 如何插入定时器来处理排序

c++ - 如何使用fetch_sub和atomic_thread_fence减少多线程?

java - 对于一个实际的开发项目,多少个 "Eclipse Projects"被认为过多?

java - 注册后自动登录

java - 动画缩放图片

java - 在设置了*中断状态*的情况下调用 Thread.sleep()?

java - StringBuilder在多线程环境下失败的实际原因是什么

java - 如何保证JMS的可靠传递

java - 在 JMS 中使用 JBoss 进行 JNDI 查找错误

java - 为什么使用 Swing BoxLayout 缩进对象