My code has many threads doing read-update-writes to a shared hash-map without any synchronization. I ran this many times with 1000 threads, 1M iterations, so I expect the final values for 4 keys will be around 250M, but not exactly, since the updates/writes may be lost, due to several threads reading the same value due to reading at the same time.
However, after many trials, I see that the values are always 250M, so no updates are being lost. FWIW, I am writing this toy application to compare it to when I use Collections.synchronizedMap, however, I need this app to fail (to show a race condition) but it always seems to produce a no-update-lost result.
Work took: 39.496
Key: 0 val: 250000000
Key: 1 val: 250000000
Key: 2 val: 250000000
Key: 3 val: 250000000
package threadsafecollections;
import java.util.HashMap;
import java.util.Map;
public class SynchronizedCollectionTask {
public static int NUM_THREADS = 1000;
public static class ReadUpdateWrite implements Runnable {
Map<Integer, Integer> map;
int threadId;
public static int NUM_ITERATIONS = 1000000;
ReadUpdateWrite(Map<Integer, Integer> m, int threadId) {
this.map = m;
this.threadId = threadId;
}
@Override
public void run() {
for (int i = 0; i < NUM_ITERATIONS; i++) {
int key = threadId % 4;
Integer val = this.map.get(key);
map.put(key, val == null ? 1 : val + 1);
}
// At this point I expect each key in map to have value of CLOSE TO
// numThreads * NUM_ITERATIONS / 4 = 250M. I expect some threads to have
// both read the same value, and therefore "miss" a write. For example,
// thread 4 and 8 both read key = 0 and see value = 3. Now both thread
// increment value to 4, instead of one thread incrementing to 4 and the
// other incrementing to 5.
}
}
public static void main(String[] args) throws InterruptedException {
Map<Integer, Integer> sharedMap = new HashMap<Integer, Integer>();
// Initialize threads
Thread[] readers = new Thread[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++) {
readers[i] = new Thread(new ReadUpdateWrite(sharedMap, i));
}
long start = System.currentTimeMillis();
// Start threads
for (int i = 0; i < NUM_THREADS; i++) {
readers[i].run();
}
// Join threads
for (int i = 0; i < NUM_THREADS; i++) {
readers[i].join();
}
long end = System.currentTimeMillis();
System.out.println("Work took: " + (end - start) / 1000D);
for (int key : sharedMap.keySet()) {
System.out.println ("Key: " + key + " val: " + sharedMap.get(key));
}
}
}
My congratulations: you've written thread-safe code, as Thread.run()
just starts internal Runnable.run
, so you invoke all operations sequentially. Use Thread.start()
instead:
This is how Thread.run()
looks like:
@Override
public void run() {
if (target != null) {
target.run();
}
}