Q1: ConcurrentModificationException does not mean what you think it
means.
thread collisions on a non-synchronized collection aren't 'detected'.
You get randomly screwed data in your map/list/set. That's how you'll
eventually 'notice'. Best case scenario you get a random unrelated
exception with probably a useless stacktrace (A real stack trace, just
pointing at code which is innocuous and not the problem).
ConcurrentModificationException occurs anytime that you're using
related things in a way that it wasn't designed for. The most likely
situation is trying to use an Iterator after you change something.
That's one of the reasons why Iterator.remove() exists; it's the only
way you get to remove things while continuing to use the iterator.
Trying to empty a map with: for ( Object x : map.keySet() )
map.remove(x); will ALWAYS generate a ConcurrentMOdificationException
even if you have one thread. In fact, threads have absolutely nothing
to do with this.
Q1 addendum: Don't use HashTable. Consider HashTable @Deprecated, and
consider Sun misguided in not actually adding the @Deprecated tag to
the java runtime libraries. HashTable is effectively the same as
Collections.synchronizedMap(new HashMap()), but 'internally
synchronized' maps, such as Collections.synchronizedMap/HashTable, are
almost never the right answer to a multi-threaded situation. For
example, let's say you want to add something to the map, but only if
the key doesn't exist yet:
if ( !map.containsKey(foo) ) map.put(foo, 0);
FAIL - EVEN if map is synchronized, that'll break in multi threaded
environments. The correct way is to do this:
synchronized ( map ) {
if ( !map.containsKey(foo) ) map.put(foo, 0);
}
and if you have to do your own synchronizing half the time, then why
even bother with the false sense of security that
Collections.synchronizedMap gives you? Threading is hard, and the
pitiful attempt to make things work right offered by HashTable/
Collections.synchronized just doesn't cut the cheese.
Q2b: No, not if you access it from multiple threads. In practice as
long as you just read it from it you have nothing to worry about, but
if you add a key in one thread while another thread is in the middle
of doing a lookup of a completely different key, odds are you'll get a
random element back, or null, eventhough the key is in there. Only
synchronizing writes won't be any help either - you'd have to sync
both the read and the write to the same locker. In general, you're
looking in the wrong place. Look at java.util.concurrent. Much better
than screwing about with 'synchronized'.
Q3: Constructor cloning of collections does not create dependencies of
any kind, it really copies everything. Of course, if your values or
keys are MUTABLE, then modifying the keys/values will also affect the
keys/values in the copied map. Using mutable things in maps is bad, by
the way. Don't - if you need to mutate a key, remove the entry from
the map, mutate the key, and add it back again. maps don't magically
update the location of a key in the hashtree, so mutating a key
without remove/readd is always a bug. Repeat after me: Immutable is
good. Immutable is good! As long as you don't add/remove anything to
the map while enumerating over it, you have no problem. try queueing
up the modifications you plan to make to the map and make them after
you're done iterating, or if you just need to remove stuff, use
Iterator.remove().
Q4: java.util.concurrent.
Q5: Only one way to do it. Lock it down (synchronize on something),
print em all out. To reduce locking time, make sure you're 'printing'
to a memory source, and not to the network or disk.
Q6: In java 1.5.0, using ++ on autoboxed generics stuff broke javac
(javac itself threw an exception, so, a bug in other words), but that
has been addressed.
i++, where i is of type Integer (or Long, Short, Byte, Character,
Long, or Float), is equivalent to:
(i = i+1)
in other words, first the 'i' gets autounboxed into an integer, then 1
is added to this integer, and as ++ is defined to do, the new value is
stored back in 'i'. Because 'i' is an Integer and not an int,
autoboxing is used to make that work. Thus i++ autounboxes and then
autoboxes. As far as I know, it is NOT guaranteed to be atomic, but
then, I don't think i++ is either, if i is an 'int'.
The fact that the SystemId's changed is the obvious tipoff that this
is happening. In fact, if the systemids didn't change, you just
discovered how to mutate Integer, Long, and friends, and that would be
a fundamental security issue, because those are supposed to be
immutable! Fortunately they are, and i++ on Integer will create a new
Integer object and assign it to 'i'.
> I was doing my 4th Java workshop (recorded herehttp://
www.ciderbob.com/babyduke/presentations) and during it I was