这是我参与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也是一种非公平锁。
锁 | 吞吐量 | 是否产生饥饿 | 唤醒阻塞线程开销 |
---|---|---|---|
公平锁 | 较小 | 所有线程都能获得锁,不会导致线程饿死 | 需要唤醒的线程数量少,开销较小 |
非公平锁 | 较大 | 可能有线程一直等待,可能导致某些线程导致饿死 | 除了第一个线程,其他都会阻塞,开销大 |