FULL PRODUCT VERSION :
java version "1.7.0_71"
Java(TM) SE Runtime Environment (build 1.7.0_71-b14)
Java HotSpot(TM) 64-Bit Server VM (build 24.71-b01, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Redhat 6; Linux 2.6.32-504.1.3.el6.x86_64 #1 SMP Fri Oct 31 11:37:10 EDT 2014 x86_64 x86_64 x86_64 GNU/Linux
A DESCRIPTION OF THE PROBLEM :
The class javax.swing.plaf.basic.BasicDirectoryModel$LoadFilesThread has a collection field called 'runnables' which has no synchronization or thread safety. This field is modified without any synchronization logic in the thread LoadFilesThread, but is looped over (without synchronization) when the public method cancelRunnables() is called (sometimes on the EventQueueThread, see stacktrace below). This leads to java.util.ConcurrentModificationException being thrown when certain race conditions are met (i.e. when it is modified and being looped over in separate threads, rare, but possible due to the fact that whenever JFileChooser.setCurrentDirection(...) is called it fires a property change on the EventQueue which can lead to Execptions if the LoadFilesThread makes a modification at the same time).
Thread safety can be fixed by either adding synchronization blocks on the runnables field in the cancelRunnables(...) method/s and anywhere the runnables collection is modified OR modifying cancelRunnables(...) method to loop over an immutable COPY of the 'runnables' collection.
Below is the stack trace showing that a call to JFileChooser.setCurrentDirectory(...) on the EventQueueThread can lead to a ConcurrentModificationException because of the lack of thread safety in the javax.swing.plaf.basic.BasicDirectoryModel$LoadFilesThread class.
java.util.ConcurrentModificationException
at java.util.Vector$Itr.checkForComodification(Vector.java:1156)
at java.util.Vector$Itr.next(Vector.java:1133)
at javax.swing.plaf.basic.BasicDirectoryModel$LoadFilesThread.cancelRunnables(BasicDirectoryModel.java:340)
at javax.swing.plaf.basic.BasicDirectoryModel$LoadFilesThread.cancelRunnables(BasicDirectoryModel.java:346)
at javax.swing.plaf.basic.BasicDirectoryModel.validateFileCache(BasicDirectoryModel.java:135)
at javax.swing.plaf.basic.BasicDirectoryModel.propertyChange(BasicDirectoryModel.java:69)
at java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:335)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:327)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:263)
at java.awt.Component.firePropertyChange(Component.java:8393)
at javax.swing.JFileChooser.setCurrentDirectory(JFileChooser.java:581)
at mtk.tools.api.ToolPathPanel.<init>(ToolPathPanel.java:73)
at mtk.tools.api.ExtToolInfo$1.run(ExtToolInfo.java:57)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:312)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:733)
at java.awt.EventQueue.access$200(EventQueue.java:103)
at java.awt.EventQueue$3.run(EventQueue.java:694)
at java.awt.EventQueue$3.run(EventQueue.java:692)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:703)
at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:159)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I would expect to NEVER get a ConcurrentModification being thrown in the BasicDirectoryModel class exception when calling JFileChooser.setCurrentDirectory() on the EventQueueThread.
I've only seen this exception once out of countless launches/uses of the application that produces the stack trace in the description, so this bug occurs rarely. But, it should never occur, and can be easily fixed. I don't think this is a bug in the application, since the stack trace shows that the swing call was done on the EDT (i.e. this is not an issue of initializing or performing operations on swing classes in background threads).
ACTUAL -
Rare occurrence of java.util.ConcurrentModificationException
ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.util.ConcurrentModificationException
at java.util.Vector$Itr.checkForComodification(Vector.java:1156)
at java.util.Vector$Itr.next(Vector.java:1133)
at javax.swing.plaf.basic.BasicDirectoryModel$LoadFilesThread.cancelRunnables(BasicDirectoryModel.java:340)
at javax.swing.plaf.basic.BasicDirectoryModel$LoadFilesThread.cancelRunnables(BasicDirectoryModel.java:346)
at javax.swing.plaf.basic.BasicDirectoryModel.validateFileCache(BasicDirectoryModel.java:135)
at javax.swing.plaf.basic.BasicDirectoryModel.propertyChange(BasicDirectoryModel.java:69)
at java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:335)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:327)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:263)
at java.awt.Component.firePropertyChange(Component.java:8393)
at javax.swing.JFileChooser.setCurrentDirectory(JFileChooser.java:581)
at mtk.tools.api.ToolPathPanel.<init>(ToolPathPanel.java:73)
at mtk.tools.api.ExtToolInfo$1.run(ExtToolInfo.java:57)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:312)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:733)
at java.awt.EventQueue.access$200(EventQueue.java:103)
at java.awt.EventQueue$3.run(EventQueue.java:694)
at java.awt.EventQueue$3.run(EventQueue.java:692)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:703)
at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:159)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)
REPRODUCIBILITY :
This bug can be reproduced rarely.