Why nonfair lock is faster than fair lock

This is the nonfair scenario:

When a thread B asks to hold a lock, if the lock is already taken by another thread A. Then the thread B will be suspended. After thread A finished his job, it releases the lock, then thread B is resumed. However, B needs a period of time to be able to actually run it’s task from where it was suspended. And this period of time could be relatively long compared to another thread C who is asking for that exact same lock at the same time. If C could acquire the lock, does C’s job and release the lock before B even “wakes up” (the time between B’s resumed and B’s able to actually run), then it’s a win-win situation for B and C. Because C gets the lock and has it’s job done and releases the lock, while B’s “waking up” and B gets the lock no later than it otherwise would have.

In the fair scenario:

The C will be queued after B.

To set the fairness of a Lock, one can use the parametered constructor. I assume the non-fair lock is faster and I did the following test - 8 threads contenting the same lock and each thread increments the shared counter 100k times. Non-fair lock is 51 times faster than fair lock.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ContentedResource {

    private Lock lock;
    private long counter = 0L;

    public ContentedResource(boolean fair) {
        lock = new ReentrantLock(fair);
    }

    public void increment() {
        lock.lock();
        try {
            counter++;
        } finally {
            lock.unlock();
        }
    }

    public long getCount() {
        lock.lock();
        try {
            return counter;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        long durationFair = testDuration(true);
        long durationNonFair = testDuration(false);
        System.out.printf("Non-fair lock is [%d] times faster than fair lock.", (int)durationFair/durationNonFair);
    }

    private static long testDuration(boolean fair) throws InterruptedException {
        ContentedResource contentedResource = new ContentedResource(fair);
        ExecutorService threadPool = Executors.newFixedThreadPool(8);

        long start = System.currentTimeMillis();
        for (int i = 0; i < 8; i++) {
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    long i = 100_000;
                    while (i-- > 0) {
                        contentedResource.increment();
                    }
                }
            });
        }
        threadPool.shutdown();
        threadPool.awaitTermination(1, TimeUnit.MINUTES);
        long duration_millisecond = System.currentTimeMillis() - start;

        System.out.printf("Fairness [%5s], count [%d], duration is [%d]ms\n", fair, contentedResource.getCount(),
                duration_millisecond);
        return duration_millisecond;
    }
}

Output:

 Fairness [ true], count [800000], duration is [7977]ms
 Fairness [false], count [800000], duration is [156]ms
 Non-fair lock is [51] times faster than fair lock.