有个小伙伴最近咨询我,前段时间他被面试官问了synchronized是公平锁还是****非公平锁?当时就蒙圈了,最后面试结果可想而知,今天我们就用一个通俗的案例加上代码来说明公平锁和非公平锁。其实公平锁这个概念是JUC工具包才有的,比如ReentrantLock才有公平锁的概念,这篇文章我们结合生活中的实例用2段代码说明ReentrantLock公平锁和非公平锁,以及证明synchronized是非公平锁的。希望对小伙伴有帮助。
公平锁:举一个简单例子,有五个同学每天必须排队去打饭,为了简单起见,我们给这五名同学没人定义一个编号,分别为编号001到编号005,这五名同学按先来后到的排队,打饭,先来的同学能先打到饭。每个同学都是一个线程,在这个过程中后来的同学是不允许插队的,这就是公平锁。
非公平锁:后来到同学不一定后打到饭,在打饭的过程中,是允许插队的,这种线程插入的行为人们认为是不公平的。举个例子,比如编号为001,002,003,004的同学先到先排队了,005最后来排队本应该排在004后面的,但是005看001正好打完饭离开,他就去插队了,也就是打饭的顺序由001->002->003->004->005变为001->005->002->003->004。其实你现在应该理解了,公平锁就是正常排队,非公平就是插队。当然你可能会有疑问?是不是005插到001的后面一定会成功,答案是不一定,这要看时机的,我们刚才说了“005看001正好打完饭离开”,下面应该是002了,可能打饭阿姨还没问002准备吃什么,就看005已经排到前面去了,那005就插队成功了,这就是时机。下面我们用程序代码来加深理解。
synchronized非公平锁 /** * @author :jiaolian * @date :Created in 2020-12-31 16:01 * @description:食堂打饭:synchronized不公平 * @modified By: * 公众号:叫练 */ public class SyncUnFairLockTest { //食堂 private static class DiningRoom { //获取食物 public void getFood() { System.out.println(Thread.currentThread().getName()+":排队中"); synchronized (this) { System.out.println(Thread.currentThread().getName()+":@@@@@@打饭中@@@@@@@"); } } } public static void main(String[] args) { DiningRoom diningRoom = new DiningRoom(); //让5个同学去打饭 for (int i=0; i<5; i++) { new Thread(()->{ diningRoom.getFood(); },"同学编号:00"+(i+1)).start(); } } }如上代码:我们定义一个内部类DiningRoom表示食堂,getFood方法里面用synchronized锁修饰this指向DiningRoom的实例对象(22行中的diningRoom对象),主类中让编号001至005五个同学同时去打饭,用于测试先排队的同学是否能先打到饭?运行程序得到其中一种执行结果如下图所示,002->004->001->003->005同学先去排队,但打饭的顺序是002->003->001->004->005,说明这里003和001两个同学插队了,插到004前面了,我们详细分析执行过程,002先抢到锁打饭了,释放了锁,本来应该是接下来是004抢到锁去打饭(因为004是比003先来排队),但003抢到锁,打饭了,释放了锁,这是第一次插队。现在还是来004抢锁,但是没抢到又被001抢到了,释放锁后才被004抢到,这是第二次插队,后面分别再是004->005抢到锁,释放锁,程序执行完毕。因为003和001插队,我们用代码证明了synchronized是非公平锁。紧接着我们来看下ReentrantLock公平锁和非公平锁。
ReentrantLock非公平锁 import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author :jiaolian * @date :Created in 2020-12-31 11:11 * @description:非公平锁测试 在获取锁的时候和再获取锁的顺序不一致; * @modified By: * 公众号:叫练 */ public class UnFairLockTest { private static final Lock LOCK = new ReentrantLock(false); //食堂 private static class DiningRoom { //获取食物 public void getFood() { try { System.out.println(Thread.currentThread().getName()+":正在排队"); LOCK.lock(); System.out.println(Thread.currentThread().getName()+":@@@@@@打饭中@@@@@@@"); } catch (Exception e) { e.printStackTrace(); } finally { LOCK.unlock(); } } } public static void main(String[] args) throws InterruptedException { DiningRoom diningRoom = new DiningRoom(); //让5个同学去打饭 for (int i=0; i<5; i++) { new Thread(()->{ diningRoom.getFood(); },"同学编号:00"+(i+1)).start(); } } }