Java Concurrency Locking Mechanisms Explained

Java Concurrency Locking Mechanisms

In Java concurrent programming, locking mechanisms are crucial for ensuring thread safety. Their primary function is to guarantee that only one thread accesses a protected resource at any given time, preventing data inconsistency and concurrent conflicts.

1. Built-in Synchronized Locks

Java's built-in lock is implemented via the synchronized keyword, which can modify methods or code blocks to ensure exclusive execution.

public class SyncCounter {
    private int value = 0;

    public synchronized void add() {
        value++;
    }

    public int getValue() {
        return value;
    }

    public static void main(String[] args) throws InterruptedException {
        SyncCounter counter = new SyncCounter();
        Runnable job = () -> {
            for (int i = 0; i < 1000; i++) {
                counter.add();
            }
        };
        Thread worker1 = new Thread(job);
        Thread worker2 = new Thread(job);
        worker1.start();
        worker2.start();
        worker1.join();
        worker2.join();
        System.out.println("Result: " + counter.getValue());
    }
}

2. ReentrantLock

ReentrantLock from the java.util.concurrent.locks package provides a more flexible locking mechanism then synchronized, supporting features like fair locking and non-blocking acquisition attempts.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockCounter {
    private int total = 0;
    private final Lock lock = new ReentrantLock();

    public void increase() {
        lock.lock();
        try {
            total++;
        } finally {
            lock.unlock();
        }
    }

    public int getTotal() {
        return total;
    }

    public static void main(String[] args) throws InterruptedException {
        LockCounter counter = new LockCounter();
        Runnable task = () -> {
            for (int j = 0; j < 1000; j++) {
                counter.increase();
            }
        };
        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Final total: " + counter.getTotal());
    }
}

3. ReadWriteLock

ReadWriteLock allows multiple threads to read concurrently while granting exclusive access for writing, which can significantly improve performance in read-heavy scenarios.

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class RWLCounter {
    private int number = 0;
    private final ReadWriteLock rwl = new ReentrantReadWriteLock();

    public void increment() {
        rwl.writeLock().lock();
        try {
            number++;
        } finally {
            rwl.writeLock().unlock();
        }
    }

    public int readNumber() {
        rwl.readLock().lock();
        try {
            return number;
        } finally {
            rwl.readLock().unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        RWLCounter demo = new RWLCounter();
        Runnable write = () -> {
            for (int k = 0; k < 1000; k++) {
                demo.increment();
            }
        };
        Runnable read = () -> {
            for (int k = 0; k < 1000; k++) {
                System.out.println("Current: " + demo.readNumber());
            }
        };
        Thread writer = new Thread(write);
        Thread reader1 = new Thread(read);
        Thread reader2 = new Thread(read);
        writer.start();
        reader1.start();
        reader2.start();
        writer.join();
        reader1.join();
        reader2.join();
        System.out.println("End value: " + demo.readNumber());
    }
}

4. Condition Variables with Locks

ReentrantLock provides Condition objects for complex thread cooordination, allowing threads to wait until a specific condition is met.

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionExample {
    private final Lock lock = new ReentrantLock();
    private final Condition cond = lock.newCondition();
    private boolean flag = false;

    public void awaitSignal() {
        lock.lock();
        try {
            while (!flag) {
                cond.await();
            }
            System.out.println("Condition satisfied, continuing...");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }

    public void triggerSignal() {
        lock.lock();
        try {
            flag = true;
            cond.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ConditionExample example = new ConditionExample();
        Thread waiter = new Thread(example::awaitSignal);
        Thread signaller = new Thread(example::triggerSignal);
        waiter.start();
        Thread.sleep(1000);
        signaller.start();
        waiter.join();
        signaller.join();
    }
}

5. StampedLock

Introduced in Java 8, StampedLock offers a efficient read-write lock with optimistic read support, using stamps to manage lock state.

import java.util.concurrent.locks.StampedLock;

public class StampedExample {
    private int counter = 0;
    private final StampedLock slock = new StampedLock();

    public void bump() {
        long stamp = slock.writeLock();
        try {
            counter++;
        } finally {
            slock.unlockWrite(stamp);
        }
    }

    public int fetch() {
        long stamp = slock.tryOptimisticRead();
        int local = counter;
        if (!slock.validate(stamp)) {
            stamp = slock.readLock();
            try {
                local = counter;
            } finally {
                slock.unlockRead(stamp);
            }
        }
        return local;
    }

    public static void main(String[] args) throws InterruptedException {
        StampedExample se = new StampedExample();
        Runnable writeJob = () -> {
            for (int m = 0; m < 1000; m++) {
                se.bump();
            }
        };
        Runnable readJob = () -> {
            for (int m = 0; m < 1000; m++) {
                System.out.println("Value: " + se.fetch());
            }
        };
        Thread writerThread = new Thread(writeJob);
        Thread readerThreadA = new Thread(readJob);
        Thread readerThreadB = new Thread(readJob);
        writerThread.start();
        readerThreadA.start();
        readerThreadB.start();
        writerThread.join();
        readerThreadA.join();
        readerThreadB.join();
        System.out.println("Final counter: " + se.fetch());
    }
}

Tags: java Concurrency Locking Synchronized ReentrantLock

Posted on Wed, 15 Jul 2026 16:29:23 +0000 by heybret