Search code examples
javajava-8synchronizedreentrantlock

Why ReentrantLock is not working while synchronized work in the demo?


I am trying to follow the ReentrantLock Example in Java, Difference between synchronized vs ReentrantLock kind of tutorial. And I have a demo started with -ea on as

public class ReentrantLockZero {
    private static ReentrantLock CountLock = new ReentrantLock();
    private static int count = 0;
    private static final int RESULT_COUNT = 10_000;

    public static void main(String... args) throws Exception {
        ThreadPoolExecutor threadPoolExecutor = getMyCachedThreadPool();
        for (int i = 0; i < RESULT_COUNT; ++i) {
            threadPoolExecutor.submit(ReentrantLockZero::getCount);
            threadPoolExecutor.submit(ReentrantLockZero::getCountUsingLock);
        }
        threadPoolExecutor.shutdown();
        threadPoolExecutor.awaitTermination(10, TimeUnit.SECONDS);
        assert count == RESULT_COUNT * 2;
    }

    private static synchronized int getCount() {
        count++;
        System.out.println(Thread.currentThread().getName() + " counting in synchronized: " + count);
        return count;
    }

    private static int getCountUsingLock() {
        CountLock.lock();
        try {
            count++;
            System.out.println(Thread.currentThread().getName() + " counting in lock: " + count);
            return count;
        } finally {
            CountLock.unlock();
        }
    }
}

When using ReentrantLock as the second method getCountUsingLock, I will get java.lang.AssertionError but when I commented them out to use synchronized, it would be okay.

Considering its ReentrantLock, I removed the CountLock defined in the class and using local lock as following, but it still not work.

private static int getCountUsingLock() {
    ReentrantLock countLock = new ReentrantLock();
    countLock.lock();
    try {
        count++;
        System.out.println(Thread.currentThread().getName() + " counting in lock: " + count);
        return count;
    } finally {
        countLock.unlock();
    }
}

What's the missed point here?

Any help will be appreciated ;)


Solution

  • Kind of fool of myself.

    It's working like that because I was actually locking on different objects.

    private static synchronized int getCount()
    

    is equal to

    private static synchronized (ReentrantLockZero.class) int getCount()
    

    while new ReentrantLock(); is always a new object and there is no way to eliminate the race condition using different locks.

    So fool of me, it's easily fixed by the following demos

    public class ReentrantLockZero {
        private static ReentrantLock CountLock = new ReentrantLock();
        private static int synchronisedCount = 0;
        private static int lockedCount = 0;
        private static final int RESULT_COUNT = 10_000;
    
        public static void main(String... args) throws Exception {
            ThreadPoolExecutor threadPoolExecutor = getMyCachedThreadPool();
            for (int i = 0; i < RESULT_COUNT; ++i) {
                threadPoolExecutor.submit(ReentrantLockZero::getSynchronisedCount);
                threadPoolExecutor.submit(ReentrantLockZero::getCountUsingLock);
            }
            threadPoolExecutor.shutdown();
            threadPoolExecutor.awaitTermination(10, TimeUnit.SECONDS);
            assert synchronisedCount == RESULT_COUNT;
            assert lockedCount == RESULT_COUNT;
        }
    
        private static synchronized int getSynchronisedCount() {
            synchronisedCount++;
            System.out.println(Thread.currentThread().getName() + " counting in synchronized: " + synchronisedCount);
            return synchronisedCount;
        }
    
        private static int getCountUsingLock() {
            CountLock.lock();
            try {
                lockedCount++;
                System.out.println(Thread.currentThread().getName() + " counting in lock: " + lockedCount);
                return lockedCount;
            } finally {
                CountLock.unlock();
            }
        }
    }
    

    Why the synchronized work? Because then there is only one lock the two methods are locking on so the race condition resolved directly.

    Kind of easily fooled by the tutorial; shame on me ;(