concurrency problem

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
vinnieb
Posts: 3
Joined: Sat Mar 22, 2014 5:54 pm
antibot: No, of course not.

concurrency problem

Post by vinnieb » Sat Mar 22, 2014 6:13 pm

Hi,

From some other posts, it looks like jfreechart isn't threadsafe. So I tried to manage it manually, but I'm still having problems.

Basically, I have a data feed coming in, and each second I want to clear down the chart each 1 second. So when new data is received (on different thread), I modify a global boolean lockingWrite to be true. Then there's a section only entered if lockingWrite is false, which calls ChartUtilities.saveChartAsPNG and then clears the series.

Error I get is below, the app doesn't actually totally crash out, just this section is no longer called (thread crashed out I guess) and so the graph gets very over populated very quickly.

Should the lockingWrite work, and I've missed something?

[java] Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
[java] at java.util.ArrayList.rangeCheck(ArrayList.java:635)
[java] at java.util.ArrayList.get(ArrayList.java:411)
[java] at org.jfree.data.xy.XYSeries.getRawDataItem(XYSeries.java:634)
[java] at org.jfree.data.xy.XYSeries.getX(XYSeries.java:645)
[java] at org.jfree.data.xy.XYSeriesCollection.getX(XYSeriesCollection.java:358)
[java] at org.jfree.data.xy.AbstractXYDataset.getXValue(AbstractXYDataset.java:77)
[java] at org.jfree.chart.renderer.xy.XYLineAndShapeRenderer.drawSecondaryPass(XYLineAndShapeRenderer.java:1121)
[java] at org.jfree.chart.renderer.xy.XYLineAndShapeRenderer.drawItem(XYLineAndShapeRenderer.java:916)
[java] at org.jfree.chart.plot.XYPlot.render(XYPlot.java:3782)
[java] at org.jfree.chart.plot.XYPlot.draw(XYPlot.java:3342)
[java] at org.jfree.chart.JFreeChart.draw(JFreeChart.java:1242)
[java] at org.jfree.chart.ChartPanel.paintComponent(ChartPanel.java:1629)
[java] at javax.swing.JComponent.paint(JComponent.java:1054)
[java] at javax.swing.JComponent.paintToOffscreen(JComponent.java:5219)
[java] at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:295)
[java] at javax.swing.RepaintManager.paint(RepaintManager.java:1249)
[java] at javax.swing.JComponent._paintImmediately(JComponent.java:5167)
[java] at javax.swing.JComponent.paintImmediately(JComponent.java:4978)
[java] at javax.swing.RepaintManager$3.run(RepaintManager.java:808)
[java] at javax.swing.RepaintManager$3.run(RepaintManager.java:796)
[java] at java.security.AccessController.doPrivileged(Native Method)
[java] at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
[java] at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:796)
[java] at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:769)
[java] at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:718)
[java] at javax.swing.RepaintManager.access$1100(RepaintManager.java:62)
[java] at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1677)
[java] at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
[java] at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:733)
[java] at java.awt.EventQueue.access$200(EventQueue.java:103)
[java] at java.awt.EventQueue$3.run(EventQueue.java:694)
[java] at java.awt.EventQueue$3.run(EventQueue.java:692)
[java] at java.security.AccessController.doPrivileged(Native Method)
[java] at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
[java] at java.awt.EventQueue.dispatchEvent(EventQueue.java:703)
[java] at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
[java] at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
[java] at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
[java] at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
[java] at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
[java] at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)
[java] Exception in thread "main" java.util.ConcurrentModificationException
[java] at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
[java] at java.util.ArrayList$Itr.next(ArrayList.java:831)
[java] at org.jfree.chart.block.BlockContainer.draw(BlockContainer.java:220)
[java] at org.jfree.chart.title.LegendTitle.draw(LegendTitle.java:591)
[java] at org.jfree.chart.JFreeChart.drawTitle(JFreeChart.java:1336)
[java] at org.jfree.chart.JFreeChart.draw(JFreeChart.java:1227)
[java] at org.jfree.chart.JFreeChart.createBufferedImage(JFreeChart.java:1412)
[java] at org.jfree.chart.JFreeChart.createBufferedImage(JFreeChart.java:1392)
[java] at org.jfree.chart.ChartUtilities.writeChartAsPNG(ChartUtilities.java:183)
[java] at org.jfree.chart.ChartUtilities.saveChartAsPNG(ChartUtilities.java:310)
[java] at org.jfree.chart.ChartUtilities.saveChartAsPNG(ChartUtilities.java:285)
[java] at edu.bath.rdfUtils.rdfSUMO.SUMOMonitorRDF.run(Unknown Source)
[java] at edu.bath.rdfUtils.rdfSUMO.SUMOMonitorRDF.main(Unknown Source)

Thanks!

John Matthews
Posts: 513
Joined: Wed Sep 12, 2007 3:18 pm

Re: concurrency problem

Post by John Matthews » Sun Mar 23, 2014 12:18 am

As shown here, you can use a SwingWorker to gather data in the doInBackground() method and update your dataset in the process() implementation. A listening ChartPanel will update itself in response.

vinnieb
Posts: 3
Joined: Sat Mar 22, 2014 5:54 pm
antibot: No, of course not.

Re: concurrency problem

Post by vinnieb » Sun Mar 23, 2014 2:28 pm

Hi,

OK, looks like this is whats needed, but, I'm not quite sure how to implement. I've looked around all the other posts about SwingWorkers, and looked through the trail too, its a lot to take in.. I wondered if I could get a pointer in the right direction.

The background is, I've got an app based on jfreechart hooked up to a simulation. The simulation publishes a whole load of vehicle positions each simulation step, which are what I'm trying to plot. The sim also publishes its current sim time value. So, the trigger to stop plotting any more data, drop a CSV file and PNG image of the current graph, and then clear the graph (ready for the next time step of data) is a new simTime being received.

In the app run method, a sim sensor is started which runs on its own thread, and has a handler which is called whenever new sim data arrives. So, when vehicle speeds are received, I update the series with allSpeedSeries1.add(routeDistance,vehSpeed) but now when a new sim time is received, I call updateGraph() where I think the SwingWorker should do something..

What I'm pretty confused about, is the doInBackground(). Essentially, in the current setup, all the data has already been gathered (and is displayed to the plot as expected). Which is great, as I can see the plot in realtime as the sim is running.

From what you're saying, I think I should rework the updateGraph() section, to include a SwingWorker<Void, XYSeries> worker = new SwingWorker<Void, XYSeries>(), but then I get unstuck - do I use the current series as temp holders, and copy them over to the plot series during doInBackground()? And then where do I put the createCSV, createPNG, clearSeries steps? If that all goes in the process() step, the graph will instantly disappear? (Though I might get correct png and csv files).

Thanks for any suggestions

John Matthews
Posts: 513
Joined: Wed Sep 12, 2007 3:18 pm

Re: concurrency problem

Post by John Matthews » Tue Mar 25, 2014 11:41 am

Collect simulation data in your implementation of doInBackground(), and publish() the results. Your implementation of process() will see the results, where it's safe to upstate the chart's dataset.

vinnieb
Posts: 3
Joined: Sat Mar 22, 2014 5:54 pm
antibot: No, of course not.

Re: concurrency problem

Post by vinnieb » Tue Apr 01, 2014 8:13 pm

Hi,

Hmm.. now things seem to be running really slowly. I should be getting updates every 0.5 seconds, but everything seems to lock up after a couple of updates.

I've pasted below main block of code that I've now added to try to use SwingWorker. The context is, I have another thread which is connected to the simulation network, which is adding readings to a CopyOnWriteArrayList list. When a reading is received which is a new simulation time step it calls updateGraph(), where I want to dump out that list of readings to the series, create a PNG and CSV file of the current graph, then clear everything ready for the next simulation step. The simulation is slowly adding more and more vehicles into the sim, and after about 20 (i.e. 20 readings now being received every 0.5 seconds) things just lock.

Is there some problem with creating a SwingWorker every 0.5 secs? Or some mistake below? I couldn't really see much to do in the doInBackground() section - the simulation thread is handling all the background updates already, so I just only update the series once its in the process() method. When I was searching for similar things, I found this http://www.jfree.org/phpBB2/viewtopic.php?f=3&t=28956 post, should I explore this instead?

Code: Select all

public void updateGraph()
	{
		SwingWorker<Void, Double> worker = new SwingWorker<Void, Double>()  
		{
			@Override
			protected Void doInBackground() throws Exception 
			{
				double newfNum = 1000000+simTime;
				publish(newfNum);
			        return null;              
           		}

           		@Override
           		protected void process(List<Double> chunks) 
			{
               			for(Double simT : chunks)
				{
                   			gapChart.setTitle("Vehicle gaps at " + (simTime/1000) + "s");
               			}
				
				for (NameDataPair checkPair : gap3List)
				{
					gapSeries3.add(checkPair.getX(), checkPair.getY());
				}
				gap3List.clear();

				double newfNum = 1000000+simTime;
				dumpResults(newfNum);
				
				gapSeries3.clear();
           		}
       		};
       		worker.execute();
	}

John Matthews
Posts: 513
Joined: Wed Sep 12, 2007 3:18 pm

Re: concurrency problem

Post by John Matthews » Wed Apr 02, 2014 3:06 am

A single instance of SwingWorker should be sufficient. It's OK to sleep in doInBackground() to pace the animation.

Locked