|
|||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Objectcom.arsdigita.developersupport.Comodifications
Provides tools for debugging concurrent modification
exceptions that may occur in multi-threaded
programs. This class should be used for debugging only.
Let's illustrate by example. A concurrent modification exception will
occur if you call Iterator.next()
on an iterator
whose underlying collection
has changed after the iterator
was created. Here's the simplest example:
import java.util.*; public final class CoMoExample { public final static void main(String[] s) { List list = new LinkedList(); list.add("foo"); for (Iterator it=list.iterator(); it.hasNext(); ) { System.out.println(it.next()); list.add("bar"); } } }
If you compile CoMoExample.java
and run it, you'll get
something like this:
$ java -cp . CoMoExample foo Exception in thread "main" java.util.ConcurrentModificationException at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:530) at java.util.LinkedList$ListItr.next(LinkedList.java:471) at CoMoExample.main(CoMoExample.java:8)
In this case, the program is single-threaded and the source of the
concurrent modification exception is trivially discernible. Things get more
complicated when you have two threads, one of which is iterating over a
collection, while the other is concurrently modifying the same collection.
You will get a ConcurrentModificationException
similar to the
above, but it only gives you one half of the picture. It shows what the
iterating thread was doing when the comodification was detected. It doesn't
show you the other thread that was modifying the shared collection
concurrently.
If you have a reproducible case of concurrent modification, you can debug
it by instrumenting the offending collection as follows. Say, the culprit
collection is a list
, allocated like so:
List culprit = new LinkedList();
You may edit the above piece of code like so:
List culprit = Comodifications.newUnforgetfulList(new LinkedList());
This will give an "instrumented" list that tracks concurrent modifications a lot better than your standard list. When a concurrent modification occurs, the following stack traces will be logged.
The point where the offending iterator was created. Something like this:
The iterator was created at StackTrace: [thread 19; timestamp=15:02:47.439] at Comodifications$ListHandler$IteratorImpl.(Comodifications.java:112) at Comodifications$ListHandler.invoke(Comodifications.java:72) at $Proxy0.iterator(Unknown Source) at Main$Page.dispatch(Main.java:63)
The point where the other thread comodified the shared list.
Comodification occurred at StackTrace: [thread 20; timestamp=15:02:47.459] at Comodifications$ListHandler.invoke(Comodifications.java:78) at $Proxy0.add(Unknown Source) at Main$Page.init(Main.java:56)
Note that the "instrumented" list returned by newUnforgetfulList(List)
captures a stack trace every time the list is
mutated. This may noticeably slow down all mutator methods and change the
timing, thereby accidentally eliminating the race condition are you are
trying to debug.
StackTrace
Method Summary | |
static List |
newUnforgetfulList(List list)
Wraps the passed in list inside a tracking proxy list. |
Methods inherited from class java.lang.Object |
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Method Detail |
public static List newUnforgetfulList(List list)
|
|||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |