Java锁之公平锁&非公平锁

532 阅读2分钟

这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战

介绍

Java中锁有许多分类,如公平锁非公平锁,可重入锁,自旋锁等。这边先来介绍下公平锁和非公平锁。

公平锁:多个线程按照申请锁的顺序获得锁,类似于排队买票

非公平锁:多个线程不按照申请锁的顺序获得锁,可能出现后申请先获得的情况,在高并发条件下可能会导致顺序反转和饥饿的现象

优先级反转:后申请先获得,先申请后获得

饥饿:由于某个线程或者某几个线程一直反复申请占有锁,导致等待队列里面的线程一直获取不到锁,产生饥饿。

举例

以java并发包下的ReentrantLock为例

import java.util.concurrent.locks.ReentrantLock;

public class FairLockAndUnfairLock {

    public static void main(String[] args) {
        ReentrantLock reentrantLock = new ReentrantLock();

    }
}
/**
 * Creates an instance of {@code ReentrantLock}.
 * This is equivalent to using {@code ReentrantLock(false)}.
 */
public ReentrantLock() {
    sync = new NonfairSync();
}

/**
 * Creates an instance of {@code ReentrantLock} with the
 * given fairness policy.
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

可以看到如果我们在创建ReentrantLock的时候不传参数,产生的默认是非公平锁,想要创建公平锁需要传入true

二者的区别:

公平锁:

对每个线程获取锁的规则是公平的,当某个线程想要获取锁的时候会先查看当前锁所维护的等待队列,如果等待队列为空或者当前线程是等待队列中的第一个,就获取锁,否则就加入到等待队列中。

公平锁:

每次都先尝试直接获取锁,如果尝试失败则按照类似公平锁的规则继续。

ReentrantLock和synchronized

对于ReentrantLock来说,它默认情况是非公平锁(不传参数的情况),synchronized也是一种非公平锁。

吞吐量是否产生饥饿唤醒阻塞线程开销
公平锁较小所有线程都能获得锁,不会导致线程饿死需要唤醒的线程数量少,开销较小
非公平锁较大可能有线程一直等待,可能导致某些线程导致饿死除了第一个线程,其他都会阻塞,开销大