什么是公平锁和非公平锁
- 公平锁(Fair):加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得
- 非公平锁(Nonfair):加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待
怎样设置公平锁和非公平锁
- 先看一下源码
//定义成final型的成员变量,在构造方法中进行初始化
private final Sync sync;
//无参数默认非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
//根据参数初始化为公平锁或者非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
- 由上面的源码可以看出初始化ReentrantLock时无参为非公平锁(默认的方式),传参数true为公平锁
代码示例
public class ReentrantLockTest {
private static Lock fairLock = new ReentrantLock2(true);
private static Lock unfairLock = new ReentrantLock2();
@Test
public void fair() {
System.out.println("fair version");
try {
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new Job(fairLock)) {
public String toString() {
return getName();
}
};
thread.setName("" + i);
thread.start();
}
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Test
public void unfair() {
System.out.println("unfair version");
try {
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new Job(unfairLock)) {
public String toString() {
return getName();
}
};
thread.setName("" + i);
thread.start();
}
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static class Job implements Runnable {
private Lock lock;
public Job(Lock lock) {
this.lock = lock;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
lock.lock();
try {
System.out.println("Lock by:"
+ Thread.currentThread().getName() + " and "
+ ((ReentrantLock2) lock).getQueuedThreads()
+ " waits.");
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
// 重写ReentrantLock中的getQueuedThreads()方法,获取等待的线程队列
private static class ReentrantLock2 extends ReentrantLock {
private static final long serialVersionUID = 1773716895097002072L;
public ReentrantLock2() {}
public ReentrantLock2(boolean fair) {
super(fair);
}
public Collection<Thread> getQueuedThreads() {
return super.getQueuedThreads();
}
}
}
运行结果:从下面的运行结果可以看出
unfair version
Lock by:2 and [] waits.
Lock by:2 and [4, 0, 1, 3] waits.
Lock by:2 and [4, 0, 1, 3] waits.
Lock by:2 and [4, 0, 1, 3] waits.
Lock by:2 and [4, 0, 1, 3] waits.
Lock by:3 and [4, 0, 1] waits.
Lock by:3 and [4, 0, 1] waits.
Lock by:3 and [4, 0, 1] waits.
Lock by:3 and [4, 0, 1] waits.
Lock by:3 and [4, 0, 1] waits.
Lock by:1 and [4, 0] waits.
Lock by:1 and [4, 0] waits.
Lock by:1 and [4, 0] waits.
Lock by:1 and [4, 0] waits.
Lock by:1 and [4, 0] waits.
Lock by:0 and [4] waits.
Lock by:0 and [4] waits.
Lock by:0 and [4] waits.
Lock by:0 and [4] waits.
Lock by:4 and [0] waits.
Lock by:4 and [0] waits.
Lock by:4 and [0] waits.
Lock by:4 and [0] waits.
Lock by:4 and [0] waits.
Lock by:0 and [] waits.
fair version
Lock by:0 and [] waits.
Lock by:0 and [] waits.
Lock by:1 and [0, 4, 3, 2] waits.
Lock by:2 and [1, 0, 4, 3] waits.
Lock by:3 and [2, 1, 0, 4] waits.
Lock by:4 and [3, 2, 1, 0] waits.
Lock by:0 and [4, 3, 2, 1] waits.
Lock by:1 and [0, 4, 3, 2] waits.
Lock by:2 and [1, 0, 4, 3] waits.
Lock by:3 and [2, 1, 0, 4] waits.
Lock by:4 and [3, 2, 1, 0] waits.
Lock by:0 and [4, 3, 2, 1] waits.
Lock by:1 and [0, 4, 3, 2] waits.
Lock by:2 and [1, 0, 4, 3] waits.
Lock by:3 and [2, 1, 0, 4] waits.
Lock by:4 and [3, 2, 1, 0] waits.
Lock by:0 and [4, 3, 2, 1] waits.
Lock by:1 and [4, 3, 2] waits.
Lock by:2 and [1, 4, 3] waits.
Lock by:3 and [2, 1, 4] waits.
Lock by:4 and [3, 2, 1] waits.
Lock by:1 and [4, 3, 2] waits.
Lock by:2 and [4, 3] waits.
Lock by:3 and [4] waits.
Lock by:4 and [] waits.可以看出在非公平模式下线程获取锁“插队”非常严重,当前获取锁的线程不受sync队列中等待的线程影响就获取了锁,而公平锁就很“公平”,按照sync队列中的顺序来获取锁
- 对于公平模式下锁的获取,每次都由sync队列中等待最长的线程(链表的第一个,sync队列是由尾部结点添加,当前输出的sync队列是逆序输出)获取锁
公平锁与非公平锁的吞吐量
- 线程在非公平锁模式下的吞吐量比公平锁模式下高,原因如下:
非公平锁模式下,当线程释放锁之后,快速的通过Fast通道(下面代码片段中的tryAcquire()方法)再次获取锁,就算当前sync队列中有排队等待的线程也会被忽略。这种模式,可以保证进入和退出锁的吞吐量,但是sync队列中过早排队的线程会一直处于阻塞状态,造成“饥饿”场景。而公平性锁,就是在调用中顾及当前sync队列中的等待节点(废弃了Fast通道),也就是任意请求都需要按照sync队列中既有的顺序进行,先到先得。这样很好的确保了公平性,但是可以从结果中看到,吞吐量就没有非公平的锁高了。
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
源码地址:https://github.com/jiangxlGit/steady/tree/master/steady