Reputation: 5907
I have written the following test code in Java using ReentrantReadWriteLock to understand the difference between fair and non-fair mode. However, I see in both modes the result and output is always the same. It seems it's always working in fair mode. Can anybody explain in which case fair and non-fair mode will result in different behaviors?
package lockTest;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class MyLockTest {
static private ReadWriteLock myLock = new ReentrantReadWriteLock(false);
public class Reader extends Thread {
int val_;
public Reader(int val) {
val_ = val;
}
public void run() {
if (val_ > 0) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
myLock.readLock().lock();
System.out.println(Thread.currentThread().getName() + ": Reader inside critical section - val: " + val_ + "-----");
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
myLock.readLock().unlock();
}
}
public class Writer extends Thread {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
myLock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + ": Writer inside critical section *****");
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
myLock.writeLock().unlock();
}
}
public static void main(String[] args) {
MyLockTest test1 = new MyLockTest();
MyLockTest.Reader reader1 = test1.new Reader(0);
MyLockTest.Writer writer1 = test1.new Writer();
MyLockTest.Reader reader2 = test1.new Reader(1);
reader2.start();
writer1.start();
reader1.start();
}
}
The output is always:
Thread-0: Reader inside critical section - val: 0-----
Thread-1: Writer inside critical section *****
Thread-2: Reader inside critical section - val: 1-----
The output above is what I expect to see when I change Lock creation to fair mode:
static private ReadWriteLock myLock = new ReentrantReadWriteLock(true);
For non-fair mode I would expect to see the following output:
Thread-0: Reader inside critical section - val: 0-----
Thread-2: Reader inside critical section - val: 1-----
Thread-1: Writer inside critical section *****
Upvotes: 1
Views: 363
Reputation: 2837
Using "fair" versus "non-fair" mode affects how the lock is assigned to threads in case of contention.
From the Javadoc for ReentrantReadWriteLock: using the "non-fair" mode, the order of entry to the read and write lock is unspecified, while using the "fair" mode, threads contend for entry using an approximately arrival-order policy.
We can see how using fair/non-fair affects program execution by having some thread contend over the same lock; see the program below.
Running the sample code, a ReentrantWriteLock
is contended by different threads; after 1000 lock operations, we dump how many times each thread acquired the lock.
In case USE_FAIR=false
is used, counts are random, and a possible output is:
Thread thread-B finished, count=920
Thread thread-A finished, count=79
Thread thread-D finished, count=0
Thread thread-C finished, count=0
in case USE_FAIR=true
is used, the output is always like
Thread thread-D finished, count=249
Thread thread-A finished, count=250
Thread thread-C finished, count=250
Thread thread-B finished, count=250
Sample code
package sample1;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class UseLock {
public static void main(String[] args) {
UseLock o = new UseLock();
o.go();
}
private void go() {
TotalPermits tp = new TotalPermits();
tp.lock.writeLock().lock();
Contender a = new Contender(tp, "thread-A");
Contender b = new Contender(tp, "thread-B");
Contender c = new Contender(tp, "thread-C");
Contender d = new Contender(tp, "thread-D");
a.start();
b.start();
c.start();
d.start();
tp.lock.writeLock().unlock();
}
}
class TotalPermits {
private static final boolean USE_FAIR = true;
private int count = 1_000;
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(USE_FAIR);
public boolean get() {
try {
lock.writeLock().lock();
try {
Thread.sleep(1);
} catch (InterruptedException e) { }
return --count>0;
} finally {
lock.writeLock().unlock();
}
}
}
class Contender extends Thread {
private int count = 0;
final String name;
final TotalPermits tp;
Contender(TotalPermits tp, String name) {
this.tp = tp;
this.name = name;
}
@Override
public void run() {
while ( tp.get() ) {
count++;
}
System.out.printf("Thread %s finished, count=%d%n", name, count);
}
}
Note:
The sample code above uses a "write" lock, which can only be held by one thread at a time. So we can use that to divide N permits across the contenders. On the other hand, the "read" lock can be held by multiple threads, as long as none is holding the write lock.
Upvotes: 3