Synchronization primitives are constructs in Java that allow threads to communicate and coordinate with each other in a multi-threaded application. These constructs include locks, semaphores, barriers, and monitors, among others. Here are some of the most common synchronization primitives in Java:
Locks: Locks are synchronization primitives that allow threads to acquire and release locks on shared resources. In Java, locks are implemented using the Lock interface and its implementations, such as ReentrantLock and ReentrantReadWriteLock. Locks can be used to ensure that only one thread at a time can access a shared resource.
Here’s an example of how to use a ReentrantLock in Java:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private Lock lock = new ReentrantLock();
private int counter = 0;
public void incrementCounter() {
lock.lock();
try {
counter++;
} finally {
lock.unlock();
}
}
}
Semaphores: Semaphores are synchronization primitives that allow a fixed number of threads to access a shared resource at the same time. In Java, semaphores are implemented using the Semaphore class. Semaphores can be used to limit the number of threads that can access a shared resource, such as a database connection.
Here’s an example of how to use a Semaphore in Java:
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
private Semaphore semaphore = new Semaphore(2);
private int counter = 0;
public void incrementCounter() throws InterruptedException {
semaphore.acquire();
try {
counter++;
} finally {
semaphore.release();
}
}
}
Barriers: Barriers are synchronization primitives that allow a group of threads to wait for each other to reach a certain point in the code before continuing. In Java, barriers are implemented using the CyclicBarrier class. Barriers can be used to coordinate the execution of multiple threads in a multi-stage process.
Here’s an example of how to use a CyclicBarrier in Java:
import java.util.concurrent.CyclicBarrier;
public class BarrierExample {
private CyclicBarrier barrier = new CyclicBarrier(3);
private int counter = 0;
public void incrementCounter() throws InterruptedException {
counter++;
barrier.await();
// do something else once all threads have incremented the counter
}
}
Monitors: Monitors are synchronization primitives that allow threads to wait for a condition to be true before continuing. In Java, monitors are implemented using the synchronized keyword and the wait() and notify() methods. Monitors can be used to ensure that threads do not execute certain sections of code until a certain condition has been met.
Here’s an example of how to use a monitor in Java:
public class MonitorExample {
private int counter = 0;
public synchronized void incrementCounter() throws InterruptedException {
while (counter == 10) {
wait();
}
counter++;
notifyAll();
}
}
Overall, synchronization primitives are an important part of multi-threaded programming in Java. By using locks, semaphores, barriers, and monitors, developers can ensure that their multi-threaded applications are thread-safe and efficient.