啥?小胖连公平锁 & 非公平锁都不知道?真的菜!(下)

简介: 啥?小胖连公平锁 & 非公平锁都不知道?真的菜!

以上还是刚刚的场景,狗哥继续给你们画几个图理解下公平锁。


1、狗哥今天起得比较早(人长得帅,还这么努力)。来到食堂就 CAS 判断 state 是不是 = 0,是就修改为 1,,一然后发现自己居然排第一,最后把自己设置为加锁线程,成功买早饭。


640.png


2、小钊这比昨晚帮妹子修电脑起得比较晚,来到饭堂先判断下 state 判断状态。发现 = 1,有人占用。只能灰溜溜的去排队。


640.png


3、过段时间,狗哥买完早饭,将 state 设置为 0,并且把持有锁线程设置为 null,然后去唤醒队首的小钊。


(在小钊还未醒的时刻)另一位绿藻头小民,昨晚看语气助词片看的比较晚。来了就判断 state == 0,想插队。但是公平锁规定必须队首获取锁,他发现自己不是队首,没法获取锁很尴尬。


640.png


4、终于,小钊醒了。判断 state == 0,修改为 1。此时不能忘记还要看看自己是不是队首。发现是,最后把持有锁线程修改为自己,开心的买到了早餐。


640.png


看到这里,相信大家也彻底理解了吧?公平锁的缺点就是必须队首线程获取锁。如上例子小民都 CAS 了一遍,但因为不是队首,还是得阻塞。增加了 CPU 负担。


一个特例


针对 tryLock () 方法,它不遵守设定的公平原则。


例如,当有线程执行 tryLock () 方法的时候,一旦有线程释放了锁,那么这个正在 tryLock 的线程就能获取到锁,即使设置的是公平锁模式,即使在它之前已经有其他正在等待队列中等待的线程,简单地说就是 tryLock 可以插队。


看它的源码就会发现:


public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}


这里调用的就是 nonfairTryAcquire (),表明了是不公平的,和锁本身是否是公平锁无关。


测试代码


/**
 * 描述:演示公平锁,分别展示公平和不公平的情况,非公平锁会让现在持有锁的线程优先再次获取到锁。代码借鉴自Java并发编程实战手册2.7。
 */
public class FairAndUnfair {
    public static void main(String args[]) {
        PrintQueue printQueue = new PrintQueue();
        Thread thread[] = new Thread[10];
        for (int i = 0; i < 10; i++) {
            thread[i] = new Thread(new Job(printQueue), "Thread " + i);
        }
        for (int i = 0; i < 10; i++) {
            thread[i].start();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Job implements Runnable {
    private PrintQueue printQueue;
    public Job(PrintQueue printQueue) {
        this.printQueue = printQueue;
    }
    @Override
    public void run() {
        System.out.printf("%s: Going to print a job\n", Thread.currentThread().getName());
        printQueue.printJob(new Object());
        System.out.printf("%s: The document has been printed\n", Thread.currentThread().getName());
    }
}
class PrintQueue {
    private final Lock queueLock = new ReentrantLock(false);
    public void printJob(Object document) {
        queueLock.lock();
        try {
            Long duration = (long)(Math.random() * 10000);
            System.out.printf("%s: PrintQueue: Printing a Job during %d seconds\n",
                Thread.currentThread().getName(), (duration / 1000));
            Thread.sleep(duration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            queueLock.unlock();
        }
        queueLock.lock();
        try {
            Long duration = (long)(Math.random() * 10000);
            System.out.printf("%s: PrintQueue: Printing a Job during %d seconds\n",
                Thread.currentThread().getName(), (duration / 1000));
            Thread.sleep(duration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            queueLock.unlock();
        }
    }
}


我们可以通过改变 new ReentrantLock (false) 中的参数来设置公平 / 非公平锁。以上代码在公平的情况下的输出:


Thread 0: Going to print a job
Thread 0: PrintQueue: Printing a Job during 5 seconds
Thread 1: Going to print a job
Thread 2: Going to print a job
Thread 3: Going to print a job
Thread 4: Going to print a job
Thread 5: Going to print a job
Thread 6: Going to print a job
Thread 7: Going to print a job
Thread 8: Going to print a job
Thread 9: Going to print a job
Thread 1: PrintQueue: Printing a Job during 3 seconds
Thread 2: PrintQueue: Printing a Job during 4 seconds
Thread 3: PrintQueue: Printing a Job during 3 seconds
Thread 4: PrintQueue: Printing a Job during 9 seconds
Thread 5: PrintQueue: Printing a Job during 5 seconds
Thread 6: PrintQueue: Printing a Job during 7 seconds
Thread 7: PrintQueue: Printing a Job during 3 seconds
Thread 8: PrintQueue: Printing a Job during 9 seconds
Thread 9: PrintQueue: Printing a Job during 5 seconds
Thread 0: PrintQueue: Printing a Job during 8 seconds
Thread 0: The document has been printed
Thread 1: PrintQueue: Printing a Job during 1 seconds
Thread 1: The document has been printed
Thread 2: PrintQueue: Printing a Job during 8 seconds
Thread 2: The document has been printed
Thread 3: PrintQueue: Printing a Job during 2 seconds
Thread 3: The document has been printed
Thread 4: PrintQueue: Printing a Job during 0 seconds
Thread 4: The document has been printed
Thread 5: PrintQueue: Printing a Job during 7 seconds
Thread 5: The document has been printed
Thread 6: PrintQueue: Printing a Job during 3 seconds
Thread 6: The document has been printed
Thread 7: PrintQueue: Printing a Job during 9 seconds
Thread 7: The document has been printed
Thread 8: PrintQueue: Printing a Job during 5 seconds
Thread 8: The document has been printed
Thread 9: PrintQueue: Printing a Job during 9 seconds
Thread 9: The document has been printed


可以看出,线程直接获取锁的顺序是完全公平的,先到先得。而以上代码在非公平的情况下的输出是这样的:


Thread 0: Going to print a job
Thread 0: PrintQueue: Printing a Job during 6 seconds
Thread 1: Going to print a job
Thread 2: Going to print a job
Thread 3: Going to print a job
Thread 4: Going to print a job
Thread 5: Going to print a job
Thread 6: Going to print a job
Thread 7: Going to print a job
Thread 8: Going to print a job
Thread 9: Going to print a job
Thread 0: PrintQueue: Printing a Job during 8 seconds
Thread 0: The document has been printed
Thread 1: PrintQueue: Printing a Job during 9 seconds
Thread 1: PrintQueue: Printing a Job during 8 seconds
Thread 1: The document has been printed
Thread 2: PrintQueue: Printing a Job during 6 seconds
Thread 2: PrintQueue: Printing a Job during 4 seconds
Thread 2: The document has been printed
Thread 3: PrintQueue: Printing a Job during 9 seconds
Thread 3: PrintQueue: Printing a Job during 8 seconds
Thread 3: The document has been printed
Thread 4: PrintQueue: Printing a Job during 4 seconds
Thread 4: PrintQueue: Printing a Job during 2 seconds
Thread 4: The document has been printed
Thread 5: PrintQueue: Printing a Job during 2 seconds
Thread 5: PrintQueue: Printing a Job during 5 seconds
Thread 5: The document has been printed
Thread 6: PrintQueue: Printing a Job during 2 seconds
Thread 6: PrintQueue: Printing a Job during 6 seconds
Thread 6: The document has been printed
Thread 7: PrintQueue: Printing a Job during 6 seconds
Thread 7: PrintQueue: Printing a Job during 4 seconds
Thread 7: The document has been printed
Thread 8: PrintQueue: Printing a Job during 3 seconds
Thread 8: PrintQueue: Printing a Job during 6 seconds
Thread 8: The document has been printed
Thread 9: PrintQueue: Printing a Job during 3 seconds
Thread 9: PrintQueue: Printing a Job during 5 seconds
Thread 9: The document has been printed


巨人的肩膀


相关文章
|
4月前
|
安全 Java
【多线程面试题十九】、 公平锁与非公平锁是怎么实现的?
这篇文章解释了Java中`ReentrantLock`的公平锁和非公平锁的实现原理,其中公平锁通过检查等待队列严格按顺序获取锁,而非公平锁允许新线程有更高机会立即获取锁,两者都依赖于`AbstractQueuedSynchronizer`(AQS)和`volatile`关键字以及CAS技术来确保线程安全和锁的正确同步。
【多线程面试题十九】、 公平锁与非公平锁是怎么实现的?
|
6月前
|
安全 Java 程序员
惊呆了!Java多线程里的“synchronized”竟然这么神奇!
【6月更文挑战第20天】Java的`synchronized`关键字是解决线程安全的关键,它确保同一时间只有一个线程访问同步代码。在案例中,`Counter`类的`increment`方法如果不加同步,可能会导致竞态条件。通过使用`synchronized`方法或语句块,可以防止这种情况,确保线程安全。虽然同步会带来性能影响,但它是构建并发应用的重要工具,平衡同步与性能是使用时需考虑的。了解并恰当使用`synchronized`,能有效应对多线程挑战。
22 1
|
7月前
|
安全 Java
美团一面,面试官让介绍AQS原理并手写一个同步器,直接凉了
【5月更文挑战第2天】美团一面,面试官让介绍AQS原理并手写一个同步器,直接凉了
93 6
|
7月前
|
消息中间件 安全 算法
通透!从头到脚讲明白线程锁
线程锁在分布式应用中是重中之重,当谈论线程锁时,通常指的是在多线程编程中使用的同步机制,它可以确保在同一时刻只有一个线程能够访问共享资源,从而避免竞争条件和数据不一致性问题。
318 0
|
NoSQL Java Redis
得不到你的心,就用“分布式锁”锁住你的人 码农在囧途
朋友,如果喜欢,就去表白吧,不要因为害羞,更不要因为自卑,如果现在你都还不敢表白,那么多年后,再回头来看的时候,你可能会为曾经的胆小而后悔,也可能会为错过一个人而心中久久不能释怀,所以,大胆一点,即使失败也无所谓,至少我们曾经做过,做过了就无怨无悔,在人生这条道路上,时光稍纵即逝,我们应该把握好眼前的一切,爱是一种力量,更是一种内心的慰藉,冲吧!不要因为钱不够,不要因为容貌不出中国,更不要因为身世不显赫,你只要足够勇敢,这一切都是附加品!
113 0
|
缓存 安全 Java
Java并发编程必知必会面试连环炮
Java并发编程必知必会面试连环炮
153 0
|
监控 安全 算法
这次锁面试题的连环16问,差点就跪了
这次锁面试题的连环16问,差点就跪了
224 0
|
Java 数据库
一文速通JUC中的各种锁
一文速通JUC中的各种锁
|
算法 Java 程序员
5千字详细讲解java并发编程的AQS
本文讲解AQS的组成,实现原理,应用,源码解析
5千字详细讲解java并发编程的AQS
啥?小胖连公平锁 & 非公平锁都不知道?真的菜!(上)
啥?小胖连公平锁 & 非公平锁都不知道?真的菜!
啥?小胖连公平锁 & 非公平锁都不知道?真的菜!(上)