Java锁——公平锁与非公平锁

概念

公平锁:按序排队,判断同步队列是否还有先驱节点(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();
    }
}

Java锁——公平锁与非公平锁_第1张图片Java锁——公平锁与非公平锁_第2张图片

修改new ReentrantLock()为ture,启用公平锁。此时见执行结果,线程a,b,c,d都能较平均的抢占成功

 private Lock lock = new ReentrantLock(true);

 Java锁——公平锁与非公平锁_第3张图片Java锁——公平锁与非公平锁_第4张图片

你可能感兴趣的:(JUC,java)