Skip to main content

13. Synchronization Mechanisms in Java

πŸ’‘ Java provides several synchronization mechanisms to manage access to shared resources in a concurrent environment. Below are some commonly used mechanisms:

1. Semaphores​

A semaphore is a counting synchronization mechanism that can control access to a resource by multiple threads. It maintains a set number of permits, which can be acquired or released by threads.

Usage​

  • Counting Semaphore: Allows a set number of threads to access a resource.
  • Binary Semaphore: Similar to a mutex, it can be either free (1) or locked (0).

Example:​

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
private static final Semaphore semaphore = new Semaphore(1);

public void accessResource() {
try {
semaphore.acquire(); // Acquire a permit
// Access the shared resource
System.out.println("Resource is being accessed.");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); // Release the permit
}
}
}

2. Locks​

Locks are more flexible than synchronized blocks and methods. Java provides several lock implementations in the java.util.concurrent.locks package, allowing for more advanced thread synchronization.

2.1. ReentrantLock​

A ReentrantLock allows threads to acquire a lock and can be re-entrant (a thread can acquire the lock multiple times).

Example:​

import java.util.concurrent.locks.ReentrantLock;

public class LockExample {
private final ReentrantLock lock = new ReentrantLock();

public void safeMethod() {
lock.lock(); // Acquire the lock
try {
// Access the shared resource
System.out.println("Resource is being accessed.");
} finally {
lock.unlock(); // Ensure the lock is released
}
}
}

2.2. ReadWriteLock​

A ReadWriteLock allows multiple readers or one writer at any point in time, improving concurrency for read-heavy applications.

Example:​

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockExample {
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

public void readMethod() {
rwLock.readLock().lock(); // Acquire the read lock
try {
// Read from the shared resource
System.out.println("Resource is being read.");
} finally {
rwLock.readLock().unlock(); // Release the read lock
}
}

public void writeMethod() {
rwLock.writeLock().lock(); // Acquire the write lock
try {
// Write to the shared resource
System.out.println("Resource is being written.");
} finally {
rwLock.writeLock().unlock(); // Release the write lock
}
}
}

3. CountDownLatch​

A CountDownLatch allows one or more threads to wait until a set of operations being performed in other threads completes.

Example:​

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
private final CountDownLatch latch = new CountDownLatch(3); // Wait for 3 tasks

public void task() {
// Perform some work
System.out.println("Task completed.");
latch.countDown(); // Decrement the count
}

public void waitForCompletion() throws InterruptedException {
latch.await(); // Wait until the count reaches zero
System.out.println("All tasks are completed.");
}
}

4. CyclicBarrier​

A CyclicBarrier allows a set of threads to wait for each other to reach a common barrier point. It can be reused after the barrier is tripped.

Example:​

import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
private final CyclicBarrier barrier = new CyclicBarrier(3); // Wait for 3 threads

public void waitAtBarrier() {
try {
// Wait for other threads
System.out.println("Waiting at the barrier.");
barrier.await(); // Wait for the barrier
System.out.println("Passed the barrier.");
} catch (Exception e) {
e.printStackTrace();
}
}
}

5. Phaser​

A Phaser is a more flexible synchronization barrier than a CyclicBarrier, allowing multiple phases of computation where threads can register and deregister dynamically.

Example:​

import java.util.concurrent.Phaser;

public class PhaserExample {
private final Phaser phaser = new Phaser(1); // Register main thread

public void phaseTask() {
phaser.arriveAndAwaitAdvance(); // Wait for other threads to reach this phase
System.out.println("Phaser phase completed.");
}

public void startPhases() {
// Register other threads dynamically
phaser.register();
// Call phaseTask() in multiple threads
}
}

Summary of Synchronization Mechanisms​

MechanismDescription
SemaphoreControls access with permits; allows multiple threads.
LockMore flexible synchronization mechanism (e.g., ReentrantLock).
ReadWriteLockAllows multiple readers or one writer at a time.
CountDownLatchAllows threads to wait until a set of operations completes.
CyclicBarrierAllows threads to wait for each other at a common point.
PhaserMore flexible barrier for multiple phases of computation.

This overview covers various synchronization mechanisms in Java to help manage concurrency and ensure thread safety.