1. 核心代码主要写的有哪些?

最近一个项目的话主要负责了用户心愿单的开发和参与了支付模块的开发

2. 最近一个项目的技术架构?

dubbo+Nginx+Nacos+SpringBoot+MyBatisPlus+SpringSecurity+Redis+MySQL+RabbitMQ+xxl-job+OSS+SMS

3. object类的常用方法列举一下

2022/2/27 面试总结 - 图1

4. equals和==的区别

1.1 基本概念区分

1) 对于==,比较的是值是否相等

如果作用于基本数据类型的变量,则直接比较其存储的值是否相等,
如果作用于引用类型的变量,则比较的是所指向的对象的地址是否相等。

2) 对于equals方法,比较的是是否是同一个对象
首先,equals()方法不能作用于基本数据类型的变量

另外,equals()方法存在于Object类中,而Object类是所有类的直接或间接父类,所以说所有类中的equals()方法都继承自Object类,在没有重写equals()方法的类中,调用equals()方法其实和使用==的效果一样,也是比较的是引用类型的变量所指向的对象的地址。

不过,Java提供的类中,有些类都重写了equals()方法,重写后的equals()方法一般都是比较两个对象的值,比如String类。

Object类equals()方法源码:

  1. public boolean equals(Object obj) {
  2. return (this == obj);
  3. }

String类equals()方法源码:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

1.2 示例

示例1:

int x = 10;
int y = 10;
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(x == y); // true
System.out.println(str1 == str2); // false
System.out.println(str1.equals(str2)); // true

示例2:

String str3 = "abc";
String str4 = "abc";
System.out.println(str3 == str4); // true

str3与str4相等的原因是用到了内存中的常量池,当运行到str3创建对象时,如果常量池中没有,就在常量池中创建一个对象”abc”,第二次创建的时候,就直接使用所以两次创建的对象其实是同一个对象,它们的地址值相等

示例3:
先定义学生Student类

public class Student {
    private int age;
    public Student(int age) {
        this.age = age;
    }
}

然后创建两个Student实例来比较

Student student1 = new Student(23);
Student student2 = new Student(23);
System.out.println(student1.equals(student2)); // false

此时equals方法调用的是基类Object类的equals()方法,也就是==比较,所以返回false。
然后我们重写下equals()方法,只要两个学生的年龄相同,就认为是同一个学生。

public class Student {
    private int age;
    public Student(int age) {
        this.age = age;
    }
    @Override
    public boolean equals(Object obj) {
        Student student = (Student) obj;
        return this.age == student.age;
    }
}

此时再比较刚刚的两个实例,返回true。

Student student1 = new Student(23);
Student student2 = new Student(23);
System.out.println(student1.equals(student2)); // true

5. String,StringBuilder,StringBuffer的区别

1、在字符串不经常发生变化的业务场景优先使用String(代码更清晰简洁)。如常量的声明,少量的字符串操作(拼接,删除等)。
2、在单线程情况下,如有大量的字符串操作情况,应该使用StringBuilder来操作字符串。不能使用String”+”来拼接而是使用,避免产生大量无用的中间对象,耗费空间且执行效率低下(新建对象、回收对象花费大量时间)。如JSON的封装等。
3、在多线程情况下,如有大量的字符串操作情况,应该使用StringBuffer。如HTTP参数解析和封装等。

区别1:线程安全

StringBuffer:线程安全,StringBuilder:线程不安全。因为 StringBuffer 的所有公开方法都是 synchronized 修饰的,而 StringBuilder 并没有 synchronized 修饰。
StringBuffer 代码片段:

@Override 
public synchronized StringBuffer append(String str) {     
    toStringCache = null;     
    super.append(str);     
    return this; 
}

区别2:缓冲区

StringBuffer 代码片段:

private transient char[] toStringCache; 
@Override 
public synchronized String toString() {     
    if (toStringCache == null) {         
        toStringCache = Arrays.copyOfRange(value, 0, count);     
    }     
    return new String(toStringCache, true); 
}

StringBuilder 代码片段:

@Override public String toString() {     
    // Create a copy, don't share the array     
    return new String(value, 0, count); 
}

可以看出,StringBuffer 每次获取 toString 都会直接使用缓存区的 toStringCache 值来构造一个字符串。
而 StringBuilder 则每次都需要复制一次字符数组,再构造一个字符串。
所以,缓存冲这也是对 StringBuffer 的一个优化吧,不过 StringBuffer 的这个toString 方法仍然是同步的。

区别3:性能

既然 StringBuffer 是线程安全的,它的所有公开方法都是同步的,StringBuilder 是没有对方法加锁同步的,所以毫无疑问,StringBuilder 的性能要远大于 StringBuffer。

总结

所以,StringBuffer 适用于用在多线程操作同一个 StringBuffer 的场景,如果是单线程场合 StringBuilder 更适合。

6. StringBuffer是线程安全的吗?底层怎么实现的?sychronized锁住了什么?

  1. 是线程安全的
 public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
  1. 直接通过synchronized 关键字来实现同步操作
  • 使用环境:多线程java程序中。
  • 作用:在多线程的环境下,控制synchronized代码段不被多个线程同时执行。synchronized既可以加在一段代码上,也可以加在方法上。
  • 使用:synchronized锁住的是括号里的对象,而不是代码。对于非static的synchronized方法,锁的就是对象本身也就是this。

  • 更常用的方法是:使用schronized锁住这个类对应的Class对象

  • 使用 static synchronized一起修饰方法时,static方法可以直接类名加方法名调用,方法中无法使用this,所以它锁的不是this,而是类的Class对象,所以,static synchronized方法也相当于全局锁,相当于锁住了代码段。

7. HashMap底层原理?负载因子是多少?

数据结构:
1.8之前 由数组和链表组成 ,1.8之后由数组+链表+红黑树组成

HashMap中hash函数是怎么实现的?还有哪些hash函数的实现方式?
对于key的hashCode值做hash 操作,无符号右移16位,然后做异或运算
还有平方取中法,取余数法,伪随机数法 效率低
hash值得计算方法:
key的hashCode值 无符号右移16位 然后与hashCode值做异或运算(就是key的hashCode值高16为不变,低16位与高16位做异或得到的值作为低16位), 得到hash值 然后通过 数组长度-1 与hash值做& 运算 得到最终的hash值 ,而数组长度设置为2的n次幂-1(会自动处理成2的n次幂,见下) 可以最大程度减少结果hash值的碰撞
0007a20349180eb4173cb56c072fb49.jpg

当两个对象的hashCode相等时会怎么样?
产生hash碰撞,若key值内容相同,则替换原来的值,若不相同,则连接到链表后面,若链表长度超过8且数组长度大于64 则将链表装换成红黑树
何时发生哈希碰撞和什么是哈希碰撞,如何解决哈希碰撞?
只要两个元素的key计算的哈希码值相同就会发生哈希碰撞就会产生哈希碰撞, 1.8之前采用链表解决,1.8之后采用链表+红黑树
如果两个键的hashcode相同,如何存储键值对?
通过equals 方法比较两个键的内容,若不相同,直接添加到链表或红黑树中,若相同,则替换value

HashMap 集合的初始化容量: 必须是2的n次幂
默认为16;
为什么必须是2的n次幂?如果输入值不是2的幂比如10会怎么样?
2的n次幂能最大程度减少哈希碰撞,数据均匀分配 具体见下面hash的计算
483581f317ccd24cb7a6e81ad85850e.jpg

如果创建HashMap对象时,输入的数组长度是10,不是2的幂,HashMap通过一通位移运算和或运算得到的肯定是2的幂次数,并且是离那个数最近的数字
65a670a79740cef1b4a63d5aa829868.jpg

51b3226809b38fe2cba2322c38931d5.jpg

默认的负载因子,默认值是0.75

集合最大容量 为2的30次幂

当HashMap中的其中一个链表的对象个数如果达到了8个,此时如果数组长度没有达到64,那么HashMap会先扩容解决,如果已经达到了64,那么这个链表会变成红黑树,节点类型由Node变成TreeNode类型。当然,如果映射关系被移除后,下次执行resize方法时判断树的节点个数低于6,也会再把树转换为链表 尽量避免扩容

扩容临界值: 数组长度 * 负载因子 默认为12 注意: 不是指数组的长度而是 集合中键值对的个数

put方法的存储流程:
6719a3a3f39140c0ef429a150a57763.jpg

为何出现死循环简要说明

  • HashMap是非线程安全的,在并发场景中如果不保持足够的同步,就有可能在执行HashMap.get时进入死循环,将CPU的消耗到100%。
  • HashMap采用链表解决Hash冲突。因为是链表结构,那么就很容易形成闭合的链路,这样在循环的时候只要有线程对这个HashMap进行get操作就会产生死循环,
  • 单线程情况下,只有一个线程对HashMap的数据结构进行操作,是不可能产生闭合的回路的。
  • 只有在多线程并发的情况下才会出现这种情况,那就是在put操作的时候,如果size>initialCapacity*loadFactor,hash表进行扩容,那么这时候HashMap就会进行rehash操作,随之HashMap的结构就会很大的变化。很有可能就是在两个线程在这个时候同时触发了rehash操作,产生了闭合的回路。
  • 所以多线程并发的情况下推荐使用ConcurrentHashMap。

多线程下[HashMap]的问题:
1、多线程put操作后,get操作导致死循环。
2、多线程put非NULL元素后,get操作得到NULL值。
3、多线程put操作,导致元素丢失。

8. List集合?ArrayList和LinkedList有什么区别?

List集合实现类: ArrayList , LinkedList (Vector ,Stack)现在基本不用

ArrayList和LinkedList区别?

  • 1、数据结构不同
    ArrayList是Array(动态数组)的数据结构,LinkedList是Link(链表)的数据结构。
  • 2、效率不同
    当随机访问List(get和set操作)时,ArrayList比LinkedList的效率更高,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找。
    当对数据进行增加和删除的操作(add和remove操作)时,LinkedList比ArrayList的效率更高,因为ArrayList是数组,所以在其中进行增删操作时,会对操作点之后所有数据的下标索引造成影响,需要进行数据的移动。
  • 3、自由性不同
    ArrayList自由性较低,因为它需要手动的设置固定大小的容量,但是它的使用比较方便,只需要创建,然后添加数据,通过调用下标进行使用;而LinkedList自由性较高,能够动态的随数据量的变化而变化,但是它不便于使用
  • 4、主要控件开销不同
    ArrayList主要控件开销在于需要在List列表预留一定空间;而LinkList主要控件开销在于需要存储结点信息以及结点指针信息。

9. 平时项目中如何使用final关键字的?

1、修饰类
当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。
final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
2、修饰方法
在使用final时,注意,只有在想明确禁止该方法在子类中被覆盖的情况下才将方法设置为final。final修饰的方法表示此方法
已经是“最后的、最终的”含义,亦即此方法不能被重写(可以重载多个final修饰的方法)。此处需要注意的一点是:因为重
写的前提是子类可以从父类中继承此方法,如果父类中final修饰的方法同时访问控制权限为private,将会导致子类中不能
直接继承到此方法,因此,此时可以在子类中定义相同的方法名和参数,此时不再产生重写与final的矛盾,而是在子类中重
新定义了新的方法。(注:类的private方法会隐式地被指定为final方法。)
3、修饰变量
final修饰变量只能被赋值一次,赋值后不再改变。
final修饰成员变量,必须初始化方法。
当函数的参数类型是final时,说明该参数是只读型,不能被更改;

10. 平时有接触过OMM吗?

堆溢出

java.lang.OutOfMemoryError: Java heap space

原因
1.代码中可能存在大对象分配(如大数组)
2.可能存在内存泄漏,多次GC后,仍然无法找到一块足够大的内存容纳当前对象

解决方法
1.对于大对象分配,条件允许的话,可以扩大堆内存的分配空间,是否可以对大对象进行分割
2.对于内存泄漏,可以使用(jmap和jconsole监测),检查代码,通过 jmap 命令 dump 内存对象

内存泄漏:指对象没有被引用也没有被回收

永久代/元空间溢出
2022/2/27 面试总结 - 图7
原因
1、在Java7之前,频繁的错误使用String.intern()方法
2、类class信息太多

解决方法

  1. 增加永久代空间
  2. 重启JVM
  3. jmap dump内存对象检查

    jmap-dump:format=b,file=dump.hprof

  4. 看看程序是否存在大量反射操作

JVM内存扩展知识:

  1. @SpringBootApplication
  2. @Configuration
  3. @CompnentScan
  4. @EnableAutoConfiguration
  5. @RepositoryRestResourcepublic
  6. @Import
  7. @Value

    12. @Autwired和@Resource的区别?

    1、共同点
    两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。
    2、不同点
    (1)@Autowired
  • @Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。
  • @Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。(通过类型匹配找到多个candidate,在没有@Qualifier、@Primary注解的情况下,会使用对象名作为最后的fallback匹配)

(2)@Resource

  • @Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。

    13. 项目中分布式锁是怎么用的?

    项目中主要使用了缓存机制的分布式锁
    利用Redis的Redisson实现了分布式锁
    分布式锁实现

    14. Redisson的分布式锁底层是怎么实现的?

    锁的原理
    在Redisson中,使用key来作为是否上锁的标志,当通过getLock(String key)方法获得相应的锁之后,这个key即作为一个锁存储到Redis集群中,在接下来如果有其他的线程尝试获取名为key的锁时,便会向集群中进行查询,如果能够查到这个锁并发现相应的value的值不为0,则表示已经有其他线程申请了这个锁同时还没有释放,则当前线程进入阻塞,否则由当前线程获取这个锁并将value值加一,如果是可重入锁的话,则当前线程每获得一个自身线程的锁,就将value的值加一,而每释放一个锁则将value值减一,直到减至0,完全释放这个锁。因为底层是基于分布式的Redis集群,所以Redisson实现了分布式的锁机制。

加锁

  • 在Redisson中,加锁需要以下三个参数:
  • KEYS[1] :需要加锁的key,这里需要是字符串类型。
  • ARGV[1] :锁的超时时间,防止死锁
  • ARGV[2] :锁的唯一标识,id(UUID.randomUUID()) + “:” + threadId

    15. 文件存储的时候怎么解决垃圾文件?用户一直上传重复的文件(场景题)

16. 项目中租户的数据为什么放入Redis中实现数据隔离?

17. Redis为什么快?Redis6.0版本以后Redis是多线程的,为什么要把单线程换成多线程?

Redis是基于内存运行的高性能 K-V 数据库,官方提供的测试报告是单机可以支持约10w/s的QPS
原因
(1)完全基于内存,数据存在内存中,绝大部分请求是纯粹的内存操作,非常快速,跟传统的磁盘文件数据存储相比,避免了通过磁盘IO读取到内存这部分的开销。
(2)数据结构简单,对数据操作也简单。Redis中的数据结构是专门进行设计的,每种数据结构都有一种或多种数据结构来支持。Redis正是依赖这些灵活的数据结构,来提升读取和写入的性能。
(3)采用单线程,省去了很多上下文切换的时间以及CPU消耗,不存在竞争条件,不用去考虑各种锁的问题,不存在加锁释放锁操作,也不会出现死锁而导致的性能消耗。
(4)使用基于IO多路复用机制的线程模型,可以处理并发的链接。

切换成多线程的原因:
1、Redis6.0 之前为什么一直不使用多线程?
Redis使用单线程的可维护性高。多线程模型虽然在某些方面表现优异,但是它却引入了程序执行顺序的不确定性,带来了并发读写的一系列问题,增加了系统复杂度、同时可能存在线程切换、甚至加锁解锁、死锁造成的性能损耗。
2、Redis6.0 为什么要引入多线程呢?
因为Redis的瓶颈不在内存,而是在网络I/O模块带来CPU的耗时,所以Redis6.0的多线程是用来处理网络I/O这部分,充分利用CPU资源,减少网络I/O阻塞带来的性能损耗。

Redis6.0开启多线程的方式
默认情况下Redis是关闭多线程的,可以在conf文件进行配置开启:

io-threads-do-reads yes

io-threads 线程数

## 官方建议的线程数设置:4核的机器建议设置为2或3个线程,8核的建议设置为6个线程,线程数一定要小于机器核数,尽量不超过8个。

扩展: 引出的问题
多线程模式下,是否存在线程并发安全问题?
如图,一次redis请求,要建立连接,然后获取操作的命令,然后执行命令,最后将响应的结果写到socket上。
image.png
在redis的多线程模式下,获取、解析命令,以及输出结果着两个过程,可以配置成多线程执行的,因为它毕竟是我们定位到的主要耗时点,但是命令的执行,也就是内存操作,依然是单线程运行的。所以,Redis 的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行,也就不存在并发安全问题。

18. Redis数据类型有几种?列举一下?

共有五种

  1. String
  2. Hash
  3. list
  4. set
  5. zset

    19. 项目中Redis是单机部署还是集群?

    单机部署

    20. MySql怎么实现主键自动递增?

    AutoIncriment

    21. 如果不用分页插件,MySQL怎么实现分页?

    Limit关键字

    22. 多线程有了解吗?线程的创建方式?(四种)

    1.继承Thread类创建线程
    2.实现Runnable接口创建线程
    3.使用Callable和Future创建线程
    4.使用线程池例如用Executor框架

    23. 你们项目中用到线程池了吗?

    回答没用到,不会再问
    用到基本会问线程池的七大核心参数
    线程池的原理
    线程池的分类

    24. 为什么要用SpringBoot?

    使用SpringBoot的最大好处就是简化配置,它实现了自动化配置。
    使用SpringBoot开发的好处
    (1)简化配置,不需要编写太多的xml配置文件;
    (2)基于Spring构建,使开发者快速入门,门槛很低;
    (3)SpringBoot可以创建独立运行的应用而不需要依赖于容器;
    (4)内置tomcat服务器,不需要打包成war包,可以直接放到tomcat中运行;
    (5)提供maven极简配置,以及可视化的相关监控功能,比如性能监控,应用的健康程度等;
    (6)为微服务SpringCloud奠定了基础,使得微服务的构建变得简单;
    (7)Spring可以整合很多各式各样的框架,并能很好的集成;
    (8)活跃的社区与论坛,以及丰富的开发文档;

    25. 你能简单讲下Spring bean的生命周期吗?

    2022/2/27 面试总结 - 图9
    (1)实例化Bean

    对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。

(2)设置对象属性(依赖注入):

实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。

(3)处理Aware接口:

接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:
①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值;
②如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
③如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;

(4)BeanPostProcessor:

如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。

(5)InitializingBean 与 init-method:

如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。

(6)如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;

以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。

(7)DisposableBean:
当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;

(8)destroy-method:
最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

26. AOP呢?说一下你对AOP的理解?他的作用是什么?

27. Redis的缓存穿透?缓存击穿?缓存雪崩?分别讲一下?

  1. 缓存穿透:
    1. 描述: 缓存穿透是指缓存和数据库中都没有的数据,而用户不断的发起请求,如发起id为”1” 的数据或id为特别大不存在的数据, 这时的用户很可能是攻击者,攻击会导致数据库压力过大
    2. 解决方案:
      1. 接口层增加校验, 如 用户鉴权校验 , id做基础校验 , id<>0的直接拦截
      2. 从缓存取不到的数据,在数据库中也没有取到, 这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用) 这样可以防止攻击用户反复用同一个id暴力攻击
      3. 使用布隆过滤器
  2. 缓存击穿:
    1. 缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期) , 这时由于并发用户较多 ,同时读缓存没读到数据,又同时去数据库中取数据,引起数据库压力瞬间增大,造成过大压力
    2. 解决方案:
      1. 设置热点数据永远不过期
      2. 加互斥锁
  3. 缓存雪崩:
    1. 缓存雪崩是指缓存中数据大批量同时过期,而查询量过大,引起数据库压力过大甚至宕机 , 和缓存击穿不同的是缓存击穿指并发查同一条数据 , 缓存雪崩是不同数据都过期了,很多数据都查不到从而去数据库查询
    2. 解决方案:
      1. 缓存数据的过期时间设置随机值, 防止同一时间大面积失效的问题
      2. 如果缓存数据库是分布式部署 , 将热点数据均匀分布在不同缓存数据库中
      3. 设置热点数据永远不过期

        28. 你刚刚讲到互斥锁,互斥锁是什么?

        当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制
        线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。
        互斥锁为资源引入了一个状态: 锁定和非锁定

某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
互斥锁: 对共享数据进行锁定,保证同一时刻只能有一个线程去操作。
注意: 互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的线程需要等待,等互斥锁使用完释放后,其它等待的线程再去抢这个锁。

  • 互斥锁的作用就是保证同一时刻只能有一个线程去操作共享数据,保证共享数据不会出现错误问题
  • 使用互斥锁的好处确保某段关键代码只能由一个线程从头到尾完整地去执行
  • 使用互斥锁会影响代码的执行效率,多任务改成了单任务执行
  • 互斥锁如果没有使用好容易出现死锁的情况

    29. SpringCloud,你们用的是SpringCloud还是SpringCloud阿里巴巴?服务熔断是怎么去实现的?

    熔断机制是应对雪崩效应的一种微服务链路保护机制。我们在各种场景下都会接触到熔断这两个字。高压电路中,如果某个地方的电压过高,熔断器就会熔断,对电路进行保护。股票交易中,如果股票指数过高,也会采用熔断机制,暂停股票的交易。同样,在微服务架构中,熔断机制也是起着类似的作用。当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。
    在Spring Cloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是@HystrixCommand,Hystrix会找有这个注解的方法,并将这类方法关联到和熔断器连在一起的代理上。当前,@HystrixCommand仅当类的注解为@Service或@Component时才会发挥作用。

    30.SpringCloud阿里巴巴有几个组件?列举一下

  1. Sentinel

阿里巴巴开源产品,把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

  1. Nacos

阿里巴巴开源产品,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

  1. RocketMQ

Apache RocketMQ™ 基于 Java 的高性能、高吞吐量的分布式消息和流计算平台。

  1. Dubbo

Apache Dubbo™ 是一款高性能 Java RPC 框架。

  1. Seata

阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。

  1. Alibaba Cloud OSS

阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。

  1. Alibaba Cloud SchedulerX

阿里中间件团队开发的一款分布式任务调度产品,支持周期性的任务与固定时间点触发任务。

  1. Alibaba Cloud SMS

覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
[

](https://blog.csdn.net/qq_45047809/article/details/112672423)

Spring Cloud七大组件:1、Eureka组件,描述了服务如何进行注册,注册到哪里;2、Ribbon组件;3、Feign组件,一个声明web服务客户端;4、Hystrix组件;5、Config组件;6、Zuul组件;7、Bus组件。
2022/2/27 面试总结 - 图10