公平锁:按序排队,判断同步队列是否还有先驱节点(hasQueuedPredecessors)的存在(我前面还有人吗?),如果没有先驱节点才能获取锁
非公平锁: 先占先得,只要能抢获到同步状态就可以
问题为什么会有公平锁和非公平锁的设计?为什么默认非公平?
1. 恢复挂起的线程到真正锁的获取还是有时间差的,从开发人员来看这个时间微乎其微,但是从CPU的角度来看,这个时间差存在的还是很明显的。所以非公平锁能更充分的利用CPU的时间片,尽量减少CPU空闲状态时间
2. 使用多线程很重要的考量点是线程切换的开销,当采用非公平锁时,当1个线程请求锁获取同步状态,然后释放同步状态,因为不需要考虑是否还有前驱节点,所以刚释放锁的线程在此刻再次获取同步状态的概率就变得非常大,所以就减少了线程的开销
非公平锁的问题?
有可能导致排队的线程长时间排队,也没有机会获取到锁,这就是传说中的“锁饥饿”
什么时候公平锁?什么时候用非公平锁?
如果为了更高的吞吐量,非公平锁是比较合适的,因为节省很多线程切换时间,吞吐量自然就上去了; 否则那就用公平锁,大家公平使用。
代码示例以售票案例演示公平锁和非公平锁的情况
new ReentrantLock()默认用的是非公平锁,此时见执行结果,线程a抢占成功后,执行28次后线程b抢占成功(c,d线程都未能抢占成功)
class Ticket {
private int number = 50;
private Lock lock = new ReentrantLock();//默认用的是非公平锁
public void sale() {
lock.lock();
try {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "t" + "卖出第" + number-- +"张"+ "还剩" + number + "张");
}
} finally {
lock.unlock();
}
}
}
public class SaleTicketDemo {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(()->{
for (int i = 0; i < 55; i++) {
ticket.sale();
}
},"a").start();
new Thread(()->{
for (int i = 0; i < 55; i++) {
ticket.sale();
}
},"b").start();
new Thread(()->{
for (int i = 0; i < 55; i++) {
ticket.sale();
}
},"c").start();
new Thread(()->{
for (int i = 0; i < 55; i++) {
ticket.sale();
}
},"d").start();
}
}
修改new ReentrantLock()为ture,启用公平锁。此时见执行结果,线程a,b,c,d都能较平均的抢占成功
private Lock lock = new ReentrantLock(true);



