Hidden Java concurrency bugs


3113609768_615c40c86a_b Question: how can the following line of Java code throw the exception shown below?

priv.addAll(common);
Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException
	at java.lang.System.arraycopy(Native Method)
	at java.util.ArrayList.toArray(Unknown Source)
	at java.util.ArrayList.addAll(Unknown Source)
	at TestConcurrentList$ConsumeThread.run(TestConcurrentList.java:34)

Answer: because of bad synchronization. The scenario is the following: one thread is continuously modifying the list “common” while the second thread tries to perform the “addAll” operation on it. The testcode is shown below:

import java.util.*;

public class TestConcurrentList {
	private static List common = new ArrayList();
	
	private static class GenerateThread extends Thread {
		private List common;
		
		GenerateThread(List common) {
			this.common = common;
		}
		
		@Override
		public void run() {
			while (true) {
				common.add("foo");
				if (common.size() > 1000) common.clear();
			}
		}
	}
	
	private static class ConsumeThread extends Thread {
		private List common;
		
		ConsumeThread(List common) {
			this.common = common;
		}
		
		@Override
		public void run() {
			while (true) {
				List priv = new ArrayList();
				priv.addAll(common);
			}
		}
	}

	public static void main(String[] args) throws Exception {
		Thread gen = new GenerateThread(common),
			consume = new ConsumeThread(common);
		gen.start();
		consume.start();
		System.out.println("Waiting...");
		gen.join();
	}

}

What makes this so hard debug is that (a) the exception doesn’t say anything about concurrency (it’s not like it throws an ConcurrentModificationException), (b) the exception actually occurs in the native Java libraries and (c) the source of the concurrent modifications may not be so obvious as in the reduced test case.

Conclusion? When possible, avoid concurrency or delegate it (to an RDBMS with proper transaction / locking support for example).

PS. This bug is not found by FindBugs (admittedly the support for checking concurrency bugs is fairly low at the moment) and is dependent on the version of the runtime. I reproduced it on 1.5.14, but not on 1.6.07.

Picture taken from yimhafiz’s photostream with permission.

,

Leave a Reply

Your email address will not be published. Required fields are marked *