A fair lock grants ownership in FIFO order — the thread that has waited longest in the queue gets it next. An unfair (the default) lock allows barging: a thread arriving exactly when the lock is released can grab it ahead of threads already queued. Fairness prevents starvation but costs throughput.
How each behaves
Lock fair = new ReentrantLock(true); // FIFO, starvation-free
Lock unfair = new ReentrantLock(); // default: barging allowed
With an unfair lock, when a holder releases, the JVM may hand the lock to a newly arriving thread instead of waking and scheduling a queued one. That avoids a context switch and a window where the lock sits idle, so throughput is much higher.
With a fair lock, acquisition always checks hasQueuedPredecessors() first; if anyone is ahead in the AQS queue, the arriving thread must queue too.
Why fairness is expensive
Fairness forces the runtime to actually park and unpark threads in order, eliminating the "hand off to whoever is running now" shortcut. Each handoff then involves a wakeup and a context switch, which dominate the tiny critical section. In Doug Lea's benchmarks, fair ReentrantLock can be an order of magnitude slower under contention.
When to choose fair
Rarely. Default to unfair — it is faster and, in practice, rarely starves anyone because contention is bursty and the barging window is small. Choose fair only when you have measured starvation (a thread provably waiting an unacceptably long time) or when bounded wait time matters more than throughput, e.g. a Semaphore rationing a scarce resource where every caller must eventually get a turn.