Java Multithreading Locks - Simplified Guide

π Why Locks?
Section titled βπ Why Locks?βLocks are used to ensure only one thread accesses shared data at a time.
π§ Key Concepts
Section titled βπ§ Key Conceptsβ| Concept | Description |
|---|---|
| Critical Section | Shared code that needs exclusive access |
| Race Condition | Bug from unsynchronized access |
| Deadlock | Two threads waiting on each otherβs locks |
| Mutual Exclusion | Ensures one thread executes a section at a time |
Comparison Table
Section titled βComparison Tableβ| Feature | synchronized | ReentrantLock | ReadWriteLock | volatile |
|---|---|---|---|---|
| Thread Safety | β | β | β | β (visibility only) |
| Read/Write Split | β | β | β | β |
| Fairness | β | β (optional) | β | β |
| Manual Lock Control | β | β | β | β |
| Performance (Reads) | β οΈ | β οΈ | β | β |
π synchronized Keyword
Section titled βπ synchronized Keywordβπ Basic Use
Section titled βπ Basic Useβpublic synchronized void increment() { count++;}π Block Scope
Section titled βπ Block Scopeβsynchronized(this) { count++;}π Static Method
Section titled βπ Static Methodβpublic static synchronized void increment() { count++;}β Limitations
Section titled ββ Limitationsβ- No timeout
- No lock status check
- No fairness control
β Example
Section titled ββ Exampleβpublic class SyncExample { private int count = 0;
public synchronized void increment() { count++; }
public int getCount() { return count; }
public static void main(String[] args) throws InterruptedException { SyncExample counter = new SyncExample(); Thread t1 = new Thread(() -> { for (int i = 0; i < 1000; i++) counter.increment(); }); Thread t2 = new Thread(() -> { for (int i = 0; i < 1000; i++) counter.increment(); });
t1.start(); t2.start(); t1.join(); t2.join();
System.out.println("Count: " + counter.getCount()); }}π Output
Section titled βπ OutputβCount: 2000π§ Insight
Section titled βπ§ Insightβsynchronized ensures only one thread at a time enters the critical section. Good for simple synchronization but lacks flexibility.
π§ ReentrantLock
Section titled βπ§ ReentrantLockββ¨ Features
Section titled ββ¨ Featuresβ- Manual control (
lock(),unlock()) - Fairness support
- Timeout & interruptible locks
β Example
Section titled ββ ExampleβReentrantLock lock = new ReentrantLock();lock.lock();try { count++;} finally { lock.unlock();}β± With Timeout
Section titled ββ± With Timeoutβif (lock.tryLock(1, TimeUnit.SECONDS)) { try { // critical section } finally { lock.unlock(); }}β Example
Section titled ββ Exampleβ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 int getCount() { return count; }
public static void main(String[] args) throws InterruptedException { ReentrantLockExample example = new ReentrantLockExample(); Thread t1 = new Thread(() -> { for (int i = 0; i < 1000; i++) example.increment(); }); Thread t2 = new Thread(() -> { for (int i = 0; i < 1000; i++) example.increment(); });
t1.start(); t2.start(); t1.join(); t2.join();
System.out.println("Count: " + example.getCount()); }}π Output
Section titled βπ OutputβCount: 2000π§ Insight
Section titled βπ§ InsightβUse ReentrantLock for advanced features: tryLock, fairness, and interruptible locks.
π ReadWriteLock
Section titled βπ ReadWriteLockβπ Use Case
Section titled βπ Use CaseβMultiple readers OR one writer β good for read-heavy scenarios.
β Example
Section titled ββ ExampleβReadWriteLock rwLock = new ReentrantReadWriteLock();rwLock.readLock().lock(); // for readingrwLock.writeLock().lock(); // for writingβ Example
Section titled ββ Exampleβimport java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteExample { private int data = 0; private final ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
public void write() { rw.writeLock().lock(); try { data++; System.out.println("Written: " + data); } finally { rw.writeLock().unlock(); } }
public void read() { rw.readLock().lock(); try { System.out.println("Read: " + data); } finally { rw.readLock().unlock(); } }
public static void main(String[] args) { ReadWriteExample obj = new ReadWriteExample(); obj.write(); obj.read(); }}π Output
Section titled βπ OutputβWritten: 1Read: 1π§ Insight
Section titled βπ§ InsightβImproves performance in read-heavy environments by allowing multiple concurrent reads.
π StampedLock
Section titled βπ StampedLockβπ Feature
Section titled βπ FeatureβAllows optimistic read β great for fast non-blocking reads.
β Optimistic Read
Section titled ββ Optimistic Readβlong stamp = lock.tryOptimisticRead();int result = data;if (!lock.validate(stamp)) { stamp = lock.readLock(); result = data; lock.unlockRead(stamp);}β Example
Section titled ββ Exampleβimport java.util.concurrent.locks.StampedLock;
public class StampedLockExample { private int data = 0; private final StampedLock lock = new StampedLock();
public void write() { long stamp = lock.writeLock(); try { data++; System.out.println("Write: " + data); } finally { lock.unlockWrite(stamp); } }
public void optimisticRead() { long stamp = lock.tryOptimisticRead(); int current = data; if (!lock.validate(stamp)) { stamp = lock.readLock(); try { current = data; } finally { lock.unlockRead(stamp); } } System.out.println("Read: " + current); }
public static void main(String[] args) { StampedLockExample ex = new StampedLockExample(); ex.write(); ex.optimisticRead(); }}π Output
Section titled βπ OutputβWrite: 1Read: 1π§ Insight
Section titled βπ§ InsightβGreat for performance when contention is low, using optimistic reading to avoid locks.
π§΅ Condition Variables
Section titled βπ§΅ Condition VariablesββΈ Wait & Notify
Section titled ββΈ Wait & NotifyβReplaces wait/notify with more flexible await/signal.
β Example
Section titled ββ Exampleβlock.lock();try { while (queue.isEmpty()) { notEmpty.await(); } // process notFull.signal();} finally { lock.unlock();}π’ Semaphore
Section titled βπ’ Semaphoreβπ Controls Access
Section titled βπ Controls AccessβAllows N threads to access a resource simultaneously.
β Example
Section titled ββ ExampleβSemaphore sem = new Semaphore(3);sem.acquire();// access resourcesem.release();β Example
Section titled ββ Exampleβimport java.util.concurrent.Semaphore;
public class SemaphoreExample { private static final Semaphore sem = new Semaphore(2);
public static void main(String[] args) { Runnable task = () -> { try { System.out.println(Thread.currentThread().getName() + " waiting..."); sem.acquire(); System.out.println(Thread.currentThread().getName() + " acquired."); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + " releasing..."); sem.release(); } catch (InterruptedException e) { e.printStackTrace(); } };
for (int i = 0; i < 4; i++) new Thread(task).start(); }}π Output
Section titled βπ OutputβThread-0 waiting...Thread-1 waiting...Thread-0 acquired.Thread-1 acquired.Thread-2 waiting...Thread-3 waiting...Thread-0 releasing...Thread-1 releasing...Thread-2 acquired.Thread-3 acquired.π§ Insight
Section titled βπ§ InsightβPerfect for limiting concurrent access, such as in connection pools or rate limiters.
β Lock-Free Programming
Section titled ββ Lock-Free Programmingβπ Atomic Operations
Section titled βπ Atomic Operationsββ Example
Section titled ββ ExampleβAtomicInteger count = new AtomicInteger(0);count.incrementAndGet();β Compare-and-Set
Section titled ββ Compare-and-SetβAtomicReference<String> value = new AtomicReference<>("a");value.compareAndSet("a", "b");β Example
Section titled ββ Exampleβimport java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample { private final AtomicInteger count = new AtomicInteger(0);
public void increment() { count.incrementAndGet(); }
public int get() { return count.get(); }
public static void main(String[] args) { AtomicExample obj = new AtomicExample(); obj.increment(); System.out.println("Count: " + obj.get()); }}π Output
Section titled βπ OutputβCount: 1π§ Best Practices
Section titled βπ§ Best Practicesβ- Always use
try-finallywith manual locks - Keep critical sections small
- Avoid nested locks
- Use atomic types for simple counters
- Prefer lock-free when possible
π Summary Table
Section titled βπ Summary Tableβ| Lock Type | Use Case | Performance | Complexity |
|---|---|---|---|
| synchronized | Simple lock | β Good | π’ Easy |
| ReentrantLock | Fine control | β Great under load | π‘ Medium |
| ReadWriteLock | Read-heavy | β Excellent | π‘ Medium |
| StampedLock | High concurrency | β Best for reads | π΄ High |
| Semaphore | Limit access | β Scalable | π‘ Medium |
| Atomic Variables | Lock-free operations | β Fastest | π’ Easy |