提示:如有疑问请私信联系、下方有源代码地址,请自行拿取
前言
面试中经常会问到公平锁与非公平锁的区别,这篇文章我相信看了之后你定能消灭这个知识盲区
提示:以下是本篇文章正文内容,下面案例可供参考
一、技术介绍
1.ReentrantLock是什么?
ReentrantLock实现了Lock接口,ReentrantLock具有更好的细粒度,可以在ReentrantLock里面设置内部Condititon类,可以实现分组唤醒需要唤醒的线程,synchronized是非公平锁,而RenentrantLock既能实现非公平锁也能实现公平锁二、源码分析
1.非公平锁
直接看ReentrantLock默认非公平锁源码
重点看红框中圈出的代码,可以看到非公平锁里,判断当前锁占用状态==0直接会进行compareAndSetState尝试获取锁。若此时有线程排队,可能争夺不过资源。所以这是非公平的 在非公平锁里,因为可以直接compareAndSetState来获取锁,不需要加入队列,然后等待队列头线程唤醒再获取锁这一步骤,所以效率较公平锁快
2.公平锁
在接着看ReentrantLock公平锁源码
在公平锁中,判断当前锁占用状态等于0后,会继续判断hasQueuedPredecessors队列是否有排队的情况,如果没有才会尝试获取锁,这样可以保证FIFO原则,每一个先来的线程都可以最先获取到锁,但是增加了上下文切换与等待线程的状态变换时间,效率相较于非公平锁慢。
三、单元测试
新建 ReentrantLockTest 类 先来看公平锁的测试代码
@Test
public void testLock() throws InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(10);
//公平锁测试
for (int i = 0; i < 10; i++) {
threadPool.execute(new FairLockTest());
}
//非公平锁测试
// for (int i = 0; i < 10; i++) {
// threadPool.execute(new NonFairLockTest());
// }
threadPool.shutdown();
TimeUnit.SECONDS.sleep(5);
}
class FairLockTest implements Runnable {
/**
* 公平锁
*/
Lock fairLock = new ReentrantLock(true);
@Override
public void run() {
System.out.println("线程" + Thread.currentThread().getId() + "启动");
try {
fairLock.lock();
System.out.println("线程" + Thread.currentThread().getId() + "获得锁");
} finally {
System.out.println("线程" + Thread.currentThread().getId() + "释放锁--------");
fairLock.unlock();
}
}
}
看下执行结果
可以看到获取锁的顺序基本和线程启动顺序一致,那红框标记的我为什么框出来呢?就是想说明一点公平只是相对的公平,所有公平模式,就是如果前面有线程排队就加入队尾,但是线程本身的执行是没有顺序和优先级的,这里要划重点。
再看看非公平锁的测试代码
@Test
public void testLock() throws InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(10);
//公平锁测试
// for (int i = 0; i < 10; i++) {
// threadPool.execute(new FairLockTest());
// }
//非公平锁测试
for (int i = 0; i < 10; i++) {
threadPool.execute(new NonFairLockTest());
}
threadPool.shutdown();
TimeUnit.SECONDS.sleep(5);
}
class NonFairLockTest implements Runnable {
/**
* 非公平锁
*/
Lock nonfairLock = new ReentrantLock(false);
@Override
public void run() {
System.out.println("线程" + Thread.currentThread().getId() + "启动");
try {
nonfairLock.lock();
System.out.println("线程" + Thread.currentThread().getId() + "获得锁");
} finally {
System.out.println("线程" + Thread.currentThread().getId() + "释放锁--------");
nonfairLock.unlock();
}
}
}
直接看测试结果
可以看到,出现了后面启动的可能先获得锁的比较多,这里只开了10个线程可能不明显,当在高并发情况下可能会出现先启动的线程一直获取不到锁的情况。
总结
虽公平锁与非公平锁都有其应用场景
公平锁:相对获取锁公平,如果业务中线程处理时间要远长于线程等待,那用非公平锁其实效率并不明显,用公平锁会给业务增强很多的可控制性。
非公平锁:性能高于公平锁,首先,在恢复一个被挂起的线程与该线程真正运行之间存在着严重的延迟,而且非公平锁能更充分的利用cpu的时间片,尽量的减少cpu空闲的状态时间。
作者寄语
是不是感觉很简单?更多用法请点击下方查看源码,关注我带你揭秘更多高级用法
源码地址:点此查看源码.