JUC锁的架构原理相关面试题

本文介绍了JUC锁的基本概念,包括悲观锁、乐观锁、公平锁与非公平锁的区别,以及CAS锁的工作原理和优缺点。讨论了如何利用CAS手写锁,并提出了ABA问题及其解决方案。
摘要由CSDN通过智能技术生成

JUC锁的架构原理相关面试题

大家好,我是酷酷的韩~
酷酷的韩金群

一.什么是悲观锁?

1.站在mysql的角度分析:悲观锁就是比较悲观,当多个线程对同一行数据实现修改的时候,最后只有一个线程才能修改成功,有一个线程获取到行锁则其它线程不能够对该数据做任何修改操作,变成阻塞状态。

2.java锁方面,如果没有获取到锁,则会阻塞等待,后期唤醒锁的成本就会非常高,需要重新被cpu从就绪调度到运行状态。
比如 Synchronized、Lock

在这里插入图片描述

二.什么是乐观锁?

1.概念:
乐观锁比较乐观,通过预值或版本号比较,如果不一致的情况则通过循环控制修改,当前线程不会阻塞,是乐观的,效率比较高,但是乐观锁比较消耗cpu的资源。

2.Mysql如何实现乐观锁?
表结构新增一个版本字段 version,多个线程对同一行数据实现修改操作,提前查询当前最新的version版本号,作为update的条件查询,如果在执行update时,version版本已发生变化,则查不到该数据,也就无法修改。如果修改失败则不断重试(查询最新版本、update)。

三.公平锁与非公平锁之间的区别?

1.概念:
公平锁:根据请求锁的顺序排列,先到先得,队列方式。类似排队打饭。
非公平锁:不是根据请求的顺序排列,通过争抢的方式获取锁。(Synchronized是非公平锁)。
效率:非公平锁>公平锁。

2.公平锁的底层是如何实现的?
通过队列 链表

3.代码示例:

public static void main(String[] args) {
    ReentrantLock reentrantLock = new ReentrantLock(false);//ture是公平锁 false非公平锁
    for (int i = 0; i < 10; i++) {
        int finalI = i;
        new Thread(() -> {
            try {
                reentrantLock.lock();//获取锁
                System.out.println(Thread.currentThread().getName() + "   i=" + finalI);
            } catch (Exception e) {

            } finally {
                if (reentrantLock != null) {
                    reentrantLock.unlock();//释放锁
                }
            }
        }).start();
    }
}

(1)公平锁执行结果:
在这里插入图片描述

(2)非公平锁执行结果:
在这里插入图片描述

四.什么是锁的可重入性?

在同一个线程中锁可以不断传递,可以直接获取。不需要额外获取锁。
简单来说就是只要线程获取到了锁,不管运行的方法多么复杂都不需要额外获取锁。

五.cas锁的理解

1.cas 英文意思:compare and swap 比较和交换的意思。

2.原理:CAS有3个操作数,内存值V,旧的预期值E,要修改的新值N。当且仅当预期值E和内存值V相同时,才将内存值V修改为新值N,否则什么都不做。(原理同上mysql的version控制)。

3.cas锁是C语言实现的,通过硬件指令,保证原子性。Java是通过unsafe jni技术,原子类:AtomicBoolean、AtomicInteger、AtomicLong等使用CAS实现。

4.代码示例:
atomicInteger的compareAndSet方法 第一个参数是E(预期值) 第二个参数是N(新值)

public static void main(String[] args) {
    AtomicInteger atomicInteger = new AtomicInteger(0);//此时N=0
    boolean b1 = atomicInteger.compareAndSet(0, 5);
    boolean b2 = atomicInteger.compareAndSet(5, 6);
    System.out.println(b1);
    System.out.println(b2);
}

在这里插入图片描述

六.如何利用cas手写一把锁

1.CAS无锁机制原理:
(1)定义一个锁的状态
(2)状态值0表示没有线程获取到锁
(3)状态值1表示有现成持有锁

2.实现细节:
(1)CAS获取锁:
将该锁的状态从0改为1 表示获取锁成功
如果获取锁失败,则不会阻塞而是通过循环(自旋来控制重试)

(2)CAS释放锁:
将该锁的状态从1改为0 表示释放锁成功。

3.代码示例:

package com.hjq.demo.test;


import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.IntStream;

/**
 * @author hanjinqun
 * @date 2022/2/25
 * 使用cas手写锁
 */
public class Test05 {
    private AtomicLong atomicLong = new AtomicLong(0);
    private Thread currenThread;

    /**
     * 获取锁
     * 锁的状态 0表示没有线程持有锁
     * 1表示锁已经被线程持有
     * count用来计算自旋次数
     */
    public boolean tryLock() {
        boolean flag = false;
        int count = 0;
        while (true) {
            boolean result = atomicLong.compareAndSet(0, 1);
            if (result) {
                currenThread = Thread.currentThread();
                flag = true;
                System.out.println(currenThread.getName() + "用了" + count + "次获取到了锁");
                break;
            } else {
//                if (count > 3) {//可控制自旋次数(不管获取到锁与否,都将停止自旋)
//                    break;
//                }
                count++;
            }
        }

        return flag;
    }

    /**
     * 释放锁
     */
    public boolean unLock() {
        if (currenThread != Thread.currentThread()) {
            return false;
        }
        return atomicLong.compareAndSet(1, 0);
    }

    public static void main(String[] args) {
        Test05 test05 = new Test05();
        IntStream.range(1, 10).forEach((i) -> new Thread(() -> {
            try {
                boolean result = test05.tryLock();
                if (result) {
                    test05.currenThread = Thread.currentThread();
                    System.out.println(Thread.currentThread().getName() + "获取锁成功");
                } else {
                    System.out.println(Thread.currentThread().getName() + "获取锁失败");
                }
            } catch (Exception e) {

            } finally {
                if (test05 != null) {
                    test05.unLock();
                }
            }
        }).start());
    }
}

在这里插入图片描述

七.使用cas锁的优缺点

1.优点:
没有获取到锁的线程,会一直在用户态,不会阻塞,从而可以提高效率。

2.缺点:
通过死循环控制,消耗cpu资源比较高,需要控制自循次数,避免cpu飙高问题。

八.CAS如何解决ABA的问题?

1.ABA是如何产生的?
CAS算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会导致数据的变化。
比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功。尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。如果链表的头在变化了两次后恢复了原值,但是不代表链表就没有变化。

2.解决办法:
版本号控制,对每个变量更新的版本号码进行+1操作,利用原子操作AtomicStampedReference。代码如下:

package com.hjq.demo.test;

import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * @author hanjinqun
 * @date 2022/2/25
 * cas处理ABA问题
 */
public class Test06 {
    private static AtomicStampedReference atomicStampedReference = new AtomicStampedReference(1000, 1);

    public static void main(String[] args) {
        new Thread(() -> {
            Integer value = (Integer) atomicStampedReference.getReference();//当前值
            Integer stamp = atomicStampedReference.getStamp();
            System.out.println("当前值:" + value + ";当前版本标识:" + stamp);
            boolean flag1 = atomicStampedReference.compareAndSet(value, 500, stamp, stamp + 1);
            if (flag1) {
                System.out.println("修改成功");
                System.out.println(atomicStampedReference.getReference() + "," + atomicStampedReference.getStamp());
            } else {
                System.out.println("修改失败,版本号不一致");
            }
        }).start();
    }
}

在这里插入图片描述
只有经历沧桑,才能遇见曙光。 ------酷酷的韩~

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 、 1资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READmE.文件(md如有),本项目仅用作交流学习参考,请切勿用于商业用途。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

韩金群

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值