The previous example demonstrated managing simple counter state using atomic operations. For more complex state, we can use a synchronized keyword or a ReentrantLock to safely access data across multiple threads.
Running the program shows that the counters updated as expected:
In this Java implementation, we’ve used a ReentrantLock instead of the sync.Mutex from Go. The ReentrantLock provides similar functionality, allowing us to lock and unlock to ensure thread-safe access to shared resources.
We’ve also replaced Go’s goroutines with Java threads. The WaitGroup functionality is achieved by using the join() method on each thread, which waits for the thread to complete before moving on.
The Container class encapsulates the shared state and the locking mechanism. The inc method now uses a try-finally block to ensure that the lock is always released, even if an exception occurs.
In the main method, we create and start three threads that concurrently increment the counters. We then wait for all threads to complete before printing the final state of the counters.
This example demonstrates how to use locks in Java to safely manage concurrent access to shared state across multiple threads.