Concurrency

Locks & Synchronization

synchronized couples mutual exclusion with visibility, per object monitor, automatically released. ReentrantLock adds tryLock, timeouts, interruptibility, and fairness; ReadWriteLock and StampedLock optimize read-heavy access.
  • Every object has a monitor; synchronized(obj) / synchronized methods acquire it, re-entrantly
  • Static synchronized methods lock on the Class object — a different lock than instances
  • Keep critical sections small; never call alien methods or do I/O while holding a lock (EJ 79)
  • ReentrantLock when you need tryLock/timeout/interruptible/fair — else synchronized is fine
  • ReadWriteLock: many concurrent readers OR one writer; StampedLock adds optimistic reads
  • Lock on a private final Object lock to prevent outsiders locking your monitor
synchronized — the default tool
public class Vault {
    private final Object lock = new Object();   // private lock object: uninterferable
    private final Map<String, byte[]> secrets = new HashMap<>();

    public void store(String id, byte[] value) {
        byte[] copy = value.clone();             // work OUTSIDE the lock
        synchronized (lock) {
            secrets.put(id, copy);               // only the shared-state touch inside
        }
    }
}

Monitors are reentrant: a thread holding the lock may re-acquire it (synchronized method calling another synchronized method of the same object) — without reentrancy that would self-deadlock. The lock releases automatically on exit, exception or not. Modern JVMs make uncontended synchronized extremely cheap (biased/thin locks; see Concurrent Performance); contention, not the keyword, is what costs.

ReentrantLock — when you need the extras
private final ReentrantLock lock = new ReentrantLock();

public boolean transfer(Account to, long amount) throws InterruptedException {
    if (lock.tryLock(1, TimeUnit.SECONDS)) {     // give up instead of deadlocking
        try {
            balance -= amount;
            to.deposit(amount);
            return true;
        } finally {
            lock.unlock();                        // ALWAYS in finally
        }
    }
    return false;
}
Read-heavy structures
private final ReadWriteLock rw = new ReentrantReadWriteLock();

public Config read() {
    rw.readLock().lock();          // readers share
    try { return snapshot(); } finally { rw.readLock().unlock(); }
}
public void update(Config c) {
    rw.writeLock().lock();         // writer excludes everyone
    try { apply(c); } finally { rw.writeLock().unlock(); }
}
Sources
  • Java Concurrency in PracticeCh. 2, 13 — Thread Safety; Explicit Locks
  • Core Java, Volume I: Fundamentals (13th ed.)Ch. 10.5 — Synchronization
  • Effective Java (3rd ed.)Item 79