Mastering Java Concurrency: Deep Dive into Threads, Locks, and AQS
This comprehensive guide explores Java multithreading fundamentals, covering thread creation, lifecycle, synchronization challenges, context switching costs, deadlock detection, various lock mechanisms such as synchronized, ReentrantLock, ReadWriteLock, and the underlying AbstractQueuedSynchronizer architecture with practical code examples and performance tips.
Java Multithreading Overview
Understanding Java concurrency starts with recognizing the challenges of context switching, deadlocks, resource limits, thread creation, states, and inter‑thread communication.
Context Switching
When the CPU switches between threads, it saves the current thread’s state and loads the next one, incurring CPU, cache, and kernel overhead.
CPU cost for saving/restoring registers
Cache/TLB invalidation
Kernel‑mode transition
Reducing context switches can be achieved by limiting thread count, using lock‑free algorithms, appropriate thread‑pool sizing, CAS operations, and reusing threads.
sudo -u admin /opt/magebyte/java/bin/jstack 31177 > /home/magebyte/dump17Deadlock
A deadlock occurs when two or more threads hold locks that the other needs, causing an endless wait.
public class DeadLockExample {
Object resourceA = new Object();
Object resourceB = new Object();
public static void main(String[] args) {
DeadLockExample d = new DeadLockExample();
Thread t1 = new Thread(() -> {
synchronized (d.resourceA) {
try { Thread.sleep(1000); } catch (InterruptedException e) {}
synchronized (d.resourceB) { System.out.println("t1 acquired B"); }
}
});
Thread t2 = new Thread(() -> {
synchronized (d.resourceB) {
try { Thread.sleep(1000); } catch (InterruptedException e) {}
synchronized (d.resourceA) { System.out.println("t2 acquired A"); }
}
});
t1.start(); t2.start();
}
}Use jstack to locate waiting threads and adjust thread‑pool configuration to avoid idle workers.
Thread States
Java defines six states: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, and TERMINATED. Properly managing these states helps avoid performance pitfalls.
Synchronized Keyword
Every Java object can act as a lock. synchronized can be applied to instance methods (lock = this), static methods (lock = Class object), or code blocks (lock = specified object). The JVM uses monitorenter and monitorexit bytecode instructions to acquire and release the monitor.
Lock Interface and Implementations
The Lock interface provides explicit lock control with methods such as lock(), lockInterruptibly(), tryLock(), unlock(), and newCondition(). Implementations include ReentrantLock, ReentrantReadWriteLock, and others.
Lock lock = new ReentrantLock();
lock.lock();
try {
// critical section
} finally {
lock.unlock();
}AbstractQueuedSynchronizer (AQS)
AQS is the core framework for building locks and synchronizers. It maintains an integer state and a FIFO queue of Node objects representing waiting threads. Subclasses implement tryAcquire, tryRelease, tryAcquireShared, and tryReleaseShared to define lock semantics.
class Mutex {
private static class Sync extends AbstractQueuedSynchronizer {
protected boolean tryAcquire(int acquires) {
return compareAndSetState(0, 1);
}
protected boolean tryRelease(int releases) {
setState(0);
return true;
}
protected boolean isHeldExclusively() {
return getState() == 1;
}
}
private final Sync sync = new Sync();
public void lock() { sync.acquire(1); }
public void unlock() { sync.release(1); }
}ReentrantLock
Supports re‑entrancy by tracking the owning thread and a hold count. Two strategies exist:
Non‑fair : Threads compete directly for the lock.
Fair : Threads acquire the lock in FIFO order, using hasQueuedPredecessors() to enforce ordering.
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}ReentrantReadWriteLock
Provides separate read and write locks. The internal Sync uses the high 16 bits of state for read count and low 16 bits for write count, allowing multiple concurrent readers but exclusive writers.
Lock readLock = rwLock.readLock();
Lock writeLock = rwLock.writeLock();
readLock.lock();
try { /* read data */ } finally { readLock.unlock(); }
writeLock.lock();
try { /* modify data */ } finally { writeLock.unlock(); }Condition
Created via Lock.newCondition(), a Condition provides await(), signal(), and signalAll() methods for thread coordination, similar to Object.wait() but with explicit lock control.
Lock lock = new ReentrantLock();
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();
lock.lock();
try {
while (count == capacity) notFull.await();
// produce item
notEmpty.signal();
} finally {
lock.unlock();
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
