Параллелизм в Java с использованием синхронизированных блоков, не дающих ожидаемых результатов

Ниже приведена тривиальная Java-программа. Он имеет счетчик с именем «cnt», который увеличивается и затем добавляется в список с именем «monitor». «cnt» увеличивается несколькими потоками, а значения добавляются в «monitor» несколькими потоками.

В конце метода "go ()" cnt и monitor.size () должны иметь одинаковые значения, но они не имеют. monitor.size () имеет правильное значение.

Если вы измените код, раскомментировав один из закомментированных синхронизированных блоков и закомментировав текущий без комментариев, код даст ожидаемые результаты. Кроме того, если вы установите количество потоков (THREAD_COUNT) в 1, код даст ожидаемые результаты.

Это может быть воспроизведено только на машине с несколькими реальными ядрами.

public class ThreadTester {

    private List<Integer> monitor = new ArrayList<Integer>();
    private Integer cnt = 0;
    private static final int NUM_EVENTS = 2313;
    private final int THREAD_COUNT = 13;

    public ThreadTester() {
    }

    public void go() {
        Runnable r = new Runnable() {

            @Override
            public void run() {
                for (int ii=0; ii<NUM_EVENTS; ++ii) {
                    synchronized( monitor) {
                        synchronized(cnt) {        // <-- is this synchronized necessary?
                            monitor.add(cnt);
                        }
//                        synchronized(cnt) {
//                            cnt++;        // <-- why does moving the synchronized block to here result in the correct value for cnt?
//                        }
                    }
                    synchronized(cnt) {
                        cnt++;              // <-- why does moving the synchronized block here result in cnt being wrong?
                    }
                }
//                synchronized(cnt) {
//                    cnt += NUM_EVENTS;    // <-- moving the synchronized block here results in the correct value for cnt, no surprise
//                }
            }

        };
        Thread[] threads = new Thread[THREAD_COUNT];

        for (int ii=0; ii<THREAD_COUNT; ++ii) {
            threads[ii] = new Thread(r);
        }
        for (int ii=0; ii<THREAD_COUNT; ++ii) {
            threads[ii].start();
        }
        for (int ii=0; ii<THREAD_COUNT; ++ii) {
            try { threads[ii].join(); } catch (InterruptedException e) { }
        }

        System.out.println("Both values should be: " + NUM_EVENTS*THREAD_COUNT);
        synchronized (monitor) {
            System.out.println("monitor.size() " + monitor.size());
        }
        synchronized (cnt) {
            System.out.println("cnt " + cnt);
        }
    }

    public static void main(String[] args) {
        ThreadTester t = new ThreadTester();
        t.go();

        System.out.println("DONE");
    }    
}

Ответы на вопрос(1)

Ваш ответ на вопрос