Java Synchronization Methods
Java provides built-in monitors (synchronized), explicit
locks, atomics, and utilities in
java.util.concurrent. Each section below has a minimal
runnable example with main.
1. synchronized
Every object has an intrinsic lock (monitor). Only one thread can enter
a synchronized block or method on the same lock at a time.
public class SyncExample {
private int count = 0;
private final Object lock = new Object();
// synchronized method — lock is `this`
public synchronized void incrementMethod() {
count++;
}
public void incrementBlock() {
synchronized (lock) { // lock on explicit object
count++;
}
}
public static void main(String[] args) throws InterruptedException {
SyncExample demo = new SyncExample();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) demo.incrementMethod();
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) demo.incrementBlock();
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("count = " + demo.count); // 2000
}
}
2. ReentrantLock
Explicit lock from java.util.concurrent.locks. Same thread
can acquire it again (reentrant). Always unlock in a
finally block.
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ReentrantLockExample demo = new ReentrantLockExample();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) demo.increment();
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) demo.increment();
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("count = " + demo.count); // 2000
}
}
3. ReadWriteLock
Many threads can read concurrently; writes require an exclusive lock.
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private int value = 0;
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
public int read() {
rwLock.readLock().lock();
try {
return value;
} finally {
rwLock.readLock().unlock();
}
}
public void write(int v) {
rwLock.writeLock().lock();
try {
value = v;
} finally {
rwLock.writeLock().unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ReadWriteLockExample demo = new ReadWriteLockExample();
demo.write(42);
Thread reader = new Thread(() ->
System.out.println("read = " + demo.read())
);
Thread writer = new Thread(() -> demo.write(100));
reader.start();
writer.start();
reader.join();
writer.join();
System.out.println("final = " + demo.read()); // 100
}
}
4. volatile
Ensures a write by one thread is visible to others immediately. Does
not make count++ atomic — use
synchronized or atomics for compound updates.
public class VolatileExample {
private volatile boolean running = true;
public static void main(String[] args) throws InterruptedException {
VolatileExample demo = new VolatileExample();
Thread worker = new Thread(() -> {
while (demo.running) {
// busy wait — main will set running = false
}
System.out.println("worker stopped");
});
worker.start();
Thread.sleep(100);
demo.running = false; // visible to worker without synchronized
worker.join();
}
}
5. Semaphore
Limits how many threads can access a resource at the same time.
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
// allow at most 2 threads into the critical section
private static final Semaphore sem = new Semaphore(2);
public static void main(String[] args) {
Runnable task = () -> {
try {
sem.acquire();
System.out.println(Thread.currentThread().getName() + " entered");
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
sem.release();
System.out.println(Thread.currentThread().getName() + " left");
}
};
new Thread(task, "T1").start();
new Thread(task, "T2").start();
new Thread(task, "T3").start();
}
}
6. CountDownLatch
One or more threads wait until a counter reaches zero (one-shot).
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
Runnable worker = () -> {
System.out.println(Thread.currentThread().getName() + " done");
latch.countDown();
};
new Thread(worker, "W1").start();
new Thread(worker, "W2").start();
new Thread(worker, "W3").start();
latch.await(); // main waits until count = 0
System.out.println("All workers finished");
}
}
7. CyclicBarrier
Threads wait at a barrier until all parties arrive, then all proceed (reusable).
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3, () ->
System.out.println("All threads reached the barrier")
);
Runnable task = () -> {
try {
System.out.println(Thread.currentThread().getName() + " waiting");
barrier.await();
System.out.println(Thread.currentThread().getName() + " passed");
} catch (InterruptedException | BrokenBarrierException e) {
Thread.currentThread().interrupt();
}
};
new Thread(task, "T1").start();
new Thread(task, "T2").start();
new Thread(task, "T3").start();
}
}
8. Phaser
Flexible multi-phase barrier — threads register, arrive, and advance through phases.
import java.util.concurrent.Phaser;
public class PhaserExample {
public static void main(String[] args) {
Phaser phaser = new Phaser(1); // main is party 0
Runnable task = () -> {
phaser.register();
System.out.println(Thread.currentThread().getName() + " phase 0");
phaser.arriveAndAwaitAdvance();
System.out.println(Thread.currentThread().getName() + " phase 1");
phaser.arriveAndDeregister();
};
new Thread(task, "T1").start();
new Thread(task, "T2").start();
phaser.arriveAndAwaitAdvance(); // main waits for phase 0
System.out.println("Main: phase 0 complete");
}
}
9. AtomicInteger
Lock-free atomic read-modify from
java.util.concurrent.atomic.
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample {
private static final AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) count.incrementAndGet();
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) count.incrementAndGet();
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("count = " + count.get()); // 2000
}
}
10. BlockingQueue
Thread-safe queue — producer blocks when full, consumer blocks when empty.
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueExample {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> queue = new ArrayBlockingQueue<>(2);
Thread producer = new Thread(() -> {
try {
queue.put("msg-1");
queue.put("msg-2");
System.out.println("producer: sent 2 messages");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread consumer = new Thread(() -> {
try {
System.out.println("consumer: " + queue.take());
System.out.println("consumer: " + queue.take());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producer.start();
consumer.start();
producer.join();
consumer.join();
}
}
11. ConcurrentHashMap
Thread-safe hash map — multiple threads can read and write without external locking.
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapExample {
public static void main(String[] args) throws InterruptedException {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
Thread t1 = new Thread(() ->
map.merge("hits", 1, Integer::sum)
);
Thread t2 = new Thread(() ->
map.merge("hits", 1, Integer::sum)
);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("hits = " + map.get("hits")); // 2
}
}