Java Concurrent Modification Exception

A typical unit test to detect the concurrent exception issue; I used Collections.synchronizedList (lock on the while list for both read and write) and CopyOnWriteArrayList (make a copy on write and no lock on read); The rule is similar for HashTable (lock the whole collection on both read and write), Collections.synchronizedMap (lock the whole collection on both read and write) and ConcurrentHashMap (Segments write, which is a bit different from CopyOnWriteArrayList)

Reference: https://www.geeksforgeeks.org/java-collection-difference-between-synchronized-arraylist-and-copyonwritearraylist/

public class Tests {
    @Test
    public void test_ConcurrentModificationException_Fail1() {
        final List<Integer> list = new ArrayList<>();
        helper(list, SomeClass::doSth);
    }
 
    // this failure is fail-fast, which seems not necessary for me
    @Test
    public void test_ConcurrentModificationException_Fail2() {
        final List<Integer> list = Collections.synchronizedList(new ArrayList<>());
        helper(list, SomeClass::doSth);
    }
 
    @Test
    public void test_ConcurrentModificationException_Fail3() {
        final List<Integer> list = new ArrayList<>();
        helper(list, SomeClass::doSthSync);
    }
 
    @Test
    public void test_ConcurrentModificationException_Pass1() {
        final List<Integer> list = Collections.synchronizedList(new ArrayList<>());
        helper(list, SomeClass::doSthSync);
    }
 
    @Test
    public void test_ConcurrentModificationException_Pass2() {
        final List<Integer> list = new CopyOnWriteArrayList<>();
        helper(list, SomeClass::doSthSync);
    }
 
    @Test
    public void test_ConcurrentModificationException_Pass3() {
        final List<Integer> list = new CopyOnWriteArrayList<>();
        helper(list, SomeClass::doSth);
    }
 
    // a typical helper to expose concurrent modification exception
    // Consumer is one of the possible functional interfaces; you should use the suitable functional interface here
    // the input data is also case by case
    private static void helper(final List<Integer> list, final Consumer<List<Integer>> consumer) {
        final int count = 1000;
        //first thread is changing the data in a quick fashion
        new Thread(() -> {
            try {
                for (int i = count; i >= 1; i--) {
                    list.add(i);
                    Thread.sleep(1);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                fail("Test failed due to sleep thread InterruptedException");
            } catch (RuntimeException e) {
                e.printStackTrace();
                fail("Test failed due to RuntimeException and could be related to TFS930700");
            }
        }).start();
 
        // Meanwhile the second thread is frequently calling the tested function which uses the same data
        try {
            for (int i = 0; i < count; i++) {
                consumer.accept(list);
                Thread.sleep(1);
            }
        } catch (final InterruptedException e) {
            e.printStackTrace();
            fail("Test failed due to sleep thread InterruptedException");
        } catch (final RuntimeException e) {
            e.printStackTrace();
            fail("Test failed due to RuntimeException and could be related to TFS930700");
        }
 
        // the main thread to wait for a little longer time until the above two threads completed
        try {
            Thread.sleep(count * 2);
        } catch (final InterruptedException e) {
            e.printStackTrace();
            fail("Test failed due to sleep thread InterruptedException");
        }
 
        Assert.assertEquals(count, list.size()); // just a minor verification
    }
}
 
public class SomeClass {
 
    // unsafe
    public static void doSth(final List<Integer> list) {
        for (final int k : list) {
            if (k < 0) {
                return;
            }
        }
    }
 
    // unsafe too
    public static void doSthSync(final List<Integer> list) {
        synchronized (list) {
            for (final int k : list) {
                if (k < 0) {
                    return;
                }
            }
        }
    }
}
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License