Thread safety carries an overhead (although, in modern VM's, the overhead is much lower than when the collection framework was designed). So collections aren't thread safe unless it is specifically required, with the exception of the JDK1.1 collections - when they were designed, the philosophy was more like "let's leave as little room for error, at the cost of some performance".
We have several phases in Java API evolvement.
JDK1.1
In version 1.1 of Java, we had the data structures Vector
and Hashtable
. They are completely synchronized, providing a level of thread safety.
JDK1.2
In version 1.2 of Java, the collections framework was introduced. None of the basic collections are thread-safe (they don't synchronize any operations) : ArrayList
, LinkedList
, HashMap
, TreeMap
and the Set
implementations.
But you can obtain a synchronized version by calling Collections.synchronizedMap
, Collections.synchronizedList
, etc.
JDK1.5
In version 1.5 of Java, the java.util.concurrent
framework was introduced. They contain specialized data structured for multi-threaded use. These provide a level of thread safety.
Note that even with synchronized collections, it is possible to introduce data races; it only means that you cannot destroy the internal structure of the collections (all the invariants of the collections will be maintained)
For example, if you have a two-step process where you first check that the collection doesn't contain some element, and in the second step, you insert that element. If you don't provide your own synchronization for these two steps, you can get the element added twice if two threads do this at the same time.