JUC并发编程面试题总结

news2024/11/8 4:51:19

文章目录

  • 1、创建线程的三种方式
  • 2、线程的状态
  • 3、线程的上下文切换
  • 4、run和start的区别
  • 5、sleep和wait区别
  • 6、虚假唤醒,精确唤醒
  • 7、两阶段终止模式
  • 8、多线程下的线程安全问题
  • 9、如何解决线程安全问题
  • 10、synchornized的原理
  • 11、锁升级的机制
  • 12、锁消除
  • 13、批量重偏向
  • 14、park和unpark
  • 15、死锁的条件
  • 16、锁饥饿的问题
  • 17、ReentrantLock
  • 18、java内存模型的组成?三大特性?
  • 19、volatile关键字的作用
  • 20、什么是读写屏障?
  • 21、synchronized能否保证JMM的三大特性
  • 22、手写单例模式,懒汉式,饿汉式,双检锁模式【放在最后,一定要自己手写一遍】
  • 23、什么是CAS,乐观锁机制?
  • 24、CAS的ABA问题
  • 25、原子累加器的原理?
  • 26、什么是缓存行的伪共享问题?
  • 27、什么是不可变对象?如何设计一个不可变类
  • 28、什么是享元模式?在哪些地方有应用?
  • 29、线程池的七大参数和工作原理?
  • 30、拒绝策略?
  • 31、阻塞队列?
  • 32、常见的线程池有哪些?
  • 33、线程数量设置多少比较合适?
  • 34、线程中一些api的区别:
  • 35、如何处理线程池中的异常:
  • 36、手写线程池,了解AQS源码

根据笔记总结JUC并发编程面试题,只讲重点

1、创建线程的三种方式

关键点在于三者间的区别,runnable和callable都是经过thread包装的,不同的是runnable把创建线程和执行任务进行了分离。callable通过futureTask获取任务的结果。

2、线程的状态

在操作系统层面是5种,java的api层面是6种。
创建状态(new),运行状态(run或start)【引出4、run和start的区别】、阻塞状态(synchronized没有竞争到锁)、等待状态(例如wait方法)、有时限的等待状态(例如wait方法带有时间参数)、结束状态。
在这里插入图片描述

3、线程的上下文切换

涉及到cpu任务调度和时间片,程序计数器的概念。在多线程的环境下,某个线程运行时cpu的时间片用尽,就会发生线程上下文切换问题,程序计数器会将当前线程的操作步骤记录,等到再次轮到该线程执行时,就从记录的位置继续执行【引出8、多线程下的线程安全问题】,上下文切换不一定只发生在时间片结束,主动调用yield也可以,但是调度器可能忽略该提示。

4、run和start的区别

run是在主线程中执行,start是另启动一个线程。当调用start()方法时,Java虚拟机会在一个新的线程中调用该线程对象的run()方法。

5、sleep和wait区别

sleep不会释放锁,是thread层面的(当其他线程通过interrupt方法打断正在sleep的线程,sleep方法会抛出interruptedException【引出7、两阶段终止模式】,wait会释放锁,是对象层面的,wait通常和notify和notifyAll一起运用达到线程间的通信【引出6、虚假唤醒,精确唤醒的问题

6、虚假唤醒,精确唤醒

如果有两个线程同时使用wait方法,主线程使用notify只会随机唤醒其中的一个,可能会导致不是自己想要唤醒的线程被唤醒。推荐使用notifyAll唤醒所有,或者使用reentranlock的newCondition.await()配合signal()方法精确唤醒。

7、两阶段终止模式

核心思想在于Sleep+设置打断标记。
isInterrupted():用于检查调用该方法的线程的中断状态。
interrupted():查询当前线程的中断状态,并清除中断状态标志。

8、多线程下的线程安全问题

关键点在于,很多操作在字节码的层面不是原子性的,比如i++,在底层是分为了四步,在执行这四步的过程中,线程发生上下文的切换。读取操作不会造成线程安全问题,多线程对方法中的局部变量的写也不会造成线程安全问题,因为栈和栈之间是独立的。
在这里插入图片描述

9、如何解决线程安全问题

加锁(乐观锁和悲观锁)…【从这一条可以引出下面十条面试题

10、synchornized的原理

首先,使用synchornized需要指定一个对象,如果对象不一样,那么锁是不会生效的。其次,synchornized还可以解决可见性和有序性问题,前提是代码块必须完全被synchornized控制。
表层原理:被synchornized保护的代码块,即使时间片结束,其他线程发现有锁,也无法对资源进行操作,会陷入阻塞(任务调度器不会把时间片分配给阻塞的线程)注意:synchornized不是把并行变成串行
在这里插入图片描述
底层原理:Monitor(监视器)机制。Java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的 Mark Word 中就被设置指向 Monitor 对象的指针。(使用synchronized的动作让对象和Monitor关联。)

	 Dog dog = new Dog();
	 
	 synchronized (dog){
	            
	 }

在这里插入图片描述

11、锁升级的机制

无锁->偏向锁->轻量级锁->重量级锁
无锁状态不用解释,没加锁就是无锁。
当有A、B两个线程要访问同一个代码块时,B线程不知道什么原因一直阻塞,A线程就可以反复进入代码块,相当于还是单线程的情况,
,JVM 会在对象头里记录下A线程的 ID,之后如果该线程再次请求相同的锁,不需要再进行加锁和解锁操作,直接进入同步块。
在这里插入图片描述
就在这个时候B线程解除了阻塞,JVM 会撤销偏向锁,并将锁状态升级为轻量级锁,尝试通过CAS(Compare And Swap)操作将锁对象头的内容(原偏向锁或无锁状态)更新为当前线程的锁记录(Lock Record)。
在这里插入图片描述
如果争抢锁失败,或者此时又有更多的线程来竞争锁,锁会升级为重量级锁(走synchronized的流程)。
在这里插入图片描述

12、锁消除

如果 JVM 能够确定某个对象不会在多个线程之间共享(也就是不会逃逸到当前线程之外),那么该对象上的锁就没有必要存在,JVM 会在编译时直接将该锁操作去除,这就是锁消除。【相关面试题:jvm:逃逸分析

13、批量重偏向

当 JVM 检测到某个锁对象频繁被不同线程使用时,而这些线程都是相对固定的一组线程,那么 JVM 会进行一次批量重偏向操作,将这些对象的偏向锁重定向到新的线程组,而不是逐个撤销偏向锁。
批量重偏向的触发条件是 JVM 检测到同一批对象频繁进行偏向锁撤销,但是不是属于恶性竞争的情况(有一种情况,虽然是多线程环境,A和B线程要访问某个代码块,但是实际上是A先访问,然后B再访问,这时偏向了线程A的对象仍有机会重新偏向B)如果转换成强竞争,就会执行锁的批量撤销,并将锁升级为轻量级锁或重量级锁。

14、park和unpark

park:挂起线程 unpark:唤醒线程 和wait notify的区别,park和unpark是Thread层面的,不需要配合对象锁使用,并且可以精确唤醒。还可以先unpark再park(这种情况本次park不会生效)。

@Slf4j(topic = "c.Demo1")
public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        //park案例
        //wait,notify和notifyAll必须配合Object Monitor一起使用,park unpark不用
        //park unpark可以精准到线程单位进行唤醒
        //park unpark可以先unpark
        Thread t1 = new Thread(() -> {
            log.info("start");
            try {
//                Thread.sleep(1000);
                //可以先unpark再park
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("park");
            LockSupport.park();
            log.info("resume");
        }, "t1");
 
        t1.start();
 
        Thread.sleep(1000);
        log.debug("unpark");
        LockSupport.unpark(t1);
    }
}

和sleep的区别是等待时会释放锁。

15、死锁的条件

同一个资源只能被A或者B同一时间持有,并且A在持有资源时等待B释放另一个资源,B同理,以及A和B不能强制占有对方的资源。如何定位死锁?使用jconsole控制台连接当前进程,在线程选项卡检测死锁。【引出17、ReentrantLock
在这里插入图片描述

16、锁饥饿的问题

是非公平锁模式下的问题,典型的非公平锁(synchronized),即一个线程可能会多次竞争到锁,而有的线程则一直获取不到锁。【引出17、ReentrantLock

17、ReentrantLock

ReentrantLock是一种可重入锁,同一个线程可以多次获取同一个对象而不会被阻塞。并且支持公平锁和非公平锁两种模式。并且需要手动地加锁和解锁,还提供了条件变量(condition),可以在某个线程await后使用singal方法精确唤醒。
ReentrantLock如何解决死锁问题?利用tryLock方法,tryLock是尝试获取锁,如果获取不到就直接返回false,不会一直死等,陷入阻塞。还可以设置超时时间。

18、java内存模型的组成?三大特性?

JMM的三大特性 原子性,可见性,有序性
内存模型分为主内存和工作内存。主内存线程共享,每个线程都有自己的工作内存。存储主内存的副本。可能会导致什么问题?数据不一致的问题。
如果某个线程需要频繁从主内存中读取相同的值,JIT编译器就会将值缓存到该线程自己的工作内存中,主内存中的值一旦发生改变,在没有干预的情况下该线程读取到的还是自己工作内存中的值【引出19、volatile关键字的作用
在这里插入图片描述

19、volatile关键字的作用

被volatile修饰的变量,某个线程必须每次从主存中读取该变量的值,所以保证了可见性。同时volatile关键字也可以保证有序性【引出20、读写屏障】。不能保证原子性【引出21、synchronized能否保证JMM的三大特性?

20、什么是读写屏障?

是volatile引出的概念。被volatile关键字修饰的变量,写指令后会加入写屏障,读指令前会加入读屏障。保证在写屏障之前对共享变量的改动都同步到主存,在读屏障之后的读取都是读主存中最新的数据。
在这里插入图片描述在这里插入图片描述

21、synchronized能否保证JMM的三大特性

synchronized可以保证JMM的三大特性,在某个线程获取到锁时,会清空自己工作内存的值,并重新从主存中同步一份,释放锁时,会将自身的改动同步到主存中,从而保证了可见性和有序性。而synchronized本身就可以保证原子性。以上的前提是代码块要完全被synchronized关键字控制。

22、手写单例模式,懒汉式,饿汉式,双检锁模式【放在最后,一定要自己手写一遍】

23、什么是CAS,乐观锁机制?

CAS的意思是比较并交换,首先会从共享变量中获取旧值,在修改值之前,还会从主存中获取最新的值,并且判断旧值和现在主存中最新的值是否一致,如果一致则代表修改期间,没有别的线程对共享变量进行修改,则可以将共享变量的值设置为修改后的值。前提是共享变量需要被volatile修饰。CAS就是乐观锁的一种体现。(在读取数据,操作数据时不会加锁,而是在更新数据时才会检查)【引出24、CAS的ABA问题

24、CAS的ABA问题

即:CAS操作无法感知其他线程是否将共享变量修改,又改回去的情况。解决方式:AtomicStampedReference定义版本号。在进行CAS时不仅会检查旧值和主存中最新的值,还会检查版本号是否相同。

	static AtomicStampedReference<String> atomicStampedReference = new AtomicStampedReference<String>("A",0);
	//....
	//获取值
	String reference = atomicStampedReference.getReference();
	//获取版本号 0
	int stamp = atomicStampedReference.getStamp();
	// 在后续的操作中,除了需要获取值,还需要获取版本号。
	atomicStampedReference.compareAndSet(reference,"C",stamp,stamp+1)

如果不需要关心版本号,可以使用AtomicMarkableReference标记是否进行过修改。

AtomicMarkableReference<Bag> reference = new AtomicMarkableReference<>(bag,true);

25、原子累加器的原理?

底层使用的是分段锁,使用了一个或多个累加单元(称为Cell),每个单元存储部分的累加值。当多个线程同时更新时,它们会分散到不同的单元上进行操作,减少对单个累加单元的争夺。最终累加结果:当需要获取最终的累加值时,LongAdder.sum() 会将所有单元中的部分值加总,返回累加的最终结果。【有可能会接着问你CurrentHashMap分段锁和AQS的原理,还会引出26、什么是缓存行的伪共享问题

26、什么是缓存行的伪共享问题?

首先要知道缓存行是什么:现代CPU为了提高性能,引入了多级缓存(如L1、L2、L3)。每个缓存以缓存行为单位存储数据,通常一个缓存行的大小是64字节。当一个线程读取内存中的数据时,CPU会将包含该数据的整个缓存行加载到缓存中,这样可以减少频繁访问内存的开销。
重点:当某个线程修改了共享缓存行中的数据,其他线程对应缓存中的数据将标记为无效,触发缓存行的同步或重新加载。
在这里插入图片描述
从而引出了伪共享问题:
多个线程操作不同的变量,这些变量存储在不同的内存位置上。但是这些变量共享同一个缓存行(存在于多线程的环境下)
尽管每个线程访问的变量本身是独立的,但由于它们位于同一个缓存行中,缓存一致性协议导致每次修改都会影响其他线程,形成不必要的缓存行同步,导致性能下降。这种现象称为伪共享,因为实际上这些线程并没有真正共享同一个变量。
解决方式:通过引入额外的填充字段,使得每个变量占用独立的缓存行。
在这里插入图片描述

27、什么是不可变对象?如何设计一个不可变类

不可变类的典型是:String。类和方法都被final修饰,不能让子类继承,重写。并且进行防御性拷贝,每次都返回一个新的字符串对象,保证原有的对象不被修改。防御性拷贝属于深度拷贝

28、什么是享元模式?在哪些地方有应用?

本质:对于一定范围内相同对象的重复使用。例如Long的valueOf方法。(-128~127)
在这里插入图片描述

29、线程池的七大参数和工作原理?

核心线程数,最大线程数,应急线程存活时间,时间单位,拒绝策略,线程工厂,阻塞队列。【从此引出下面n条面试题
工作原理:首先创建线程到核心线程数量,核心线程数量满了,其他的任务放入阻塞队列,阻塞队列也满了,创建剩下任务数量的应急线程直到核心线程数+应急线程数 = 最大线程数,超过这个线程数量的任务就执行丢弃策略。【引出30、拒绝策略

30、拒绝策略?

丢弃等待时间最久的,由发起任务的线程执行,丢弃任务并抛出异常,直接丢弃不作处理,还可以自定义拒绝策略。

31、阻塞队列?

首先要明确什么是阻塞队列?是一种多线程模式下的生产者-消费者模型,生产者将消息放入阻塞队列,消费者从阻塞队列获取消息,当阻塞队列满时,生产者会阻塞,并唤醒消费者,当阻塞队列为空时,消费者会阻塞并唤醒生产者。
常见的阻塞队列有:ArrayBlockingQueue:基于数组的有界阻塞队列,队列的容量在创建时固定,不能动态扩展。它支持公平锁机制,即可以选择按插入顺序公平地处理线程。
LinkedBlockingQueue:可以指定容量也可以不指定。(最大容量受到内存限制)
PriorityBlockingQueue:没有指定容量,支持优先级排序的无界阻塞队列,队列中的元素会根据自然顺序或者自定义的比较器进行排序。

32、常见的线程池有哪些?

重点:【这一条需要结合真实项目说明,不能死背面试题。如何运用线程池,设置参数,并且可以配合CompletableFuture使用】
单线程的线程池,固定大小线程池…
什么场景用到的单线程线程池?比如需要手动执行定时任务,某个定时任务执行的时间要10分钟,但是不能页面上没反应,应该点了执行就直接返回,就可以另外开一个单线程的线程池,提交任务,先返回结果,然后让后台的另一个线程继续执行任务。
什么场景用到的固定大小的线程池?
从第三方系统拉取数据,避免系统资源耗尽,设置5个线程【引出问题33、线程数量设置多少比较合适?】。

33、线程数量设置多少比较合适?

CPU密集型:复杂的数学运算、图像处理等,线程主要在使用 CPU 进行计算,并非处理外部资源 CPU 核心数 + 1
I/O 密集型:主要是 I/O 操作,如文件读写、数据库查询、网络请求等,线程通常会在等待外部资源(磁盘、网络)时处于空闲状态。
线程数 ≈ CPU 核心数 * (1 + 线程等待时间 / 线程工作时间)

34、线程中一些api的区别:

submit和execute:submit可以得到返回的结果,而execute不关心结果。submit底层还是依靠execute工作。
invokeAll和invokeAny: invokeAll会阻塞当前线程,等待所有任务执行完成后返回结果。如果有某个任务发生异常,不会返回后面任务的结果。
invokeAny:会阻塞当前线程,但是会返回最先完成任务的结果,如果最先完成任务抛出异常,则会返回第二个完成任务的结果。
shutDown和shutDownNow:shutDown:主线程不会阻塞,已经提交任务的线程会执行完。
shutDown:不会接受新的任务,同时会将队列中的任务返回。

35、如何处理线程池中的异常:

使用try-catch处理,或利用future的get()方法。

36、手写线程池,了解AQS源码

有条件可以手写实现一个简单的线程池,以及阅读AQS非公平锁模式的源码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2228643.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

<HarmonyOS第一课>给应用添加通知和提醒的习题

无形无名者&#xff0c;万物之宗也。 虽今古不同&#xff0c;时移俗易&#xff0c;故莫不由乎此&#xff0c;以成其治者也。 故可执古之道&#xff0c;以御今之有。 上古虽远&#xff0c;其道存焉&#xff0c;故虽在今&#xff0c;可以知古始也。 天命人, 刷无聊剧本… 本文来…

RabbitMQ 高级特性——事务

文章目录 前言事务配置事务管理器加上Transactional注解 前言 前面我们学习了 RabbitMQ 的延迟队列&#xff0c;通过延迟队列可以实现生产者生产的消息不是立即被消费者消费。那么这篇文章我们将来学习 RabbitMQ 的事务。 事务 RabbitMQ 是基于 AMQP 协议实现的&#xff0c;…

如何查看电脑支持的最大内存数?

① 按一下键盘的win R 键&#xff0c;输入&#xff1a;cmd 然后&#xff0c;点击【确定】&#xff08;或者按一下回车&#xff09; 在黑色窗口输入&#xff1a;wmic memphysical get maxcapacity 如下位置显示为&#xff1a;33554432 然后把这个数字&#xff1a;33554432 连…

二十二、MySQL 8.0 主从复制原理分析与实战

文章目录 一、复制&#xff08;Replication&#xff09;1、什么是复制2、复制的方式3、复制的数据同步类型3.1、异步复制3.2、半同步复制3.3、设计理念&#xff1a;复制状态机——几乎所有的分布式存储都是这么复制数据的 4、基于binlog位点同步的主从复制原理4.1、异步复制示例…

SpringBoot 下的Excel文件损坏与内容乱码问题

序言 随着打包部署的方式的改变&#xff0c;原本正常运行的代码可能带来一些新的问题&#xff0c;比如我们现在使用SpringBoot 的方式生成Jar包直接运行&#xff0c;就会对我们再在Resource下的Excel文件产生影响&#xff0c;导入与预期不符的情况发生cuiyaonan2000163.com 比…

微信小程序生成二维码

目前是在开发小程序端 --> 微信小程序。然后接到需求&#xff1a;根据 form 表单填写内容生成二维码&#xff08;第一版&#xff1a;表单目前需要客户进行自己输入&#xff0c;然后点击生成按钮实时生成二维码&#xff0c;不需要向后端请求&#xff0c;不存如数据库&#xf…

【论文阅读笔记】VLP: A Survey on Vision-language Pre-training

目录 前言2 特征提取&#xff08;Feature extraction&#xff09;2.1.1 图象特征提取OD-based Region feature / RoIFreeze the pre-trained object detectorsGrid features&#xff08;网格特征&#xff09;CNN-GFsEnd-to-End Training&#xff08;端到端训练&#xff09;ViT-…

【科研绘图】3DMAX管状图表生成插件TubeChart使用方法

3DMAX管状图表生成插件TubeChart&#xff0c;一款用于制作3D管状图表的工具。可以自定义切片的数量以及随机或指定切片颜色。 【版本要求】 3dMax 2008及更高版本 【安装方法】 TubeChart插件无需安装&#xff0c;使用时直接拖动插件脚本文件到3dMax视口中打开即可&#xff0…

CSS浮雕效果

效果图&#xff1a; HTML源码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Documen…

英飞凌TRAVEO-II MCU能做什么?

概述 英飞凌TRAVEO™T2G微控制器基于ArmCortex-M4(单核)/M7(单核/双核)内核,提供高性能、增强的人机界面、高安全性和先进的网络协议,专为电气化、车身控制模块、网关和信息娱乐应用等广泛的汽车应用量身定制。基于单核和双核操作的强大ArmCortexM系列内核,它提供了最先…

Ant-Dseign-Pro如何去国际化及删除oneapi.json后出现程序直接结束问题的解决方案

作者&#xff1a;CSDN-PleaSure乐事 欢迎大家阅读我的博客 希望大家喜欢 使用环境&#xff1a;WebStorm 移除国际化 什么是国际化 在AntDesignPro当中&#xff0c;国际化就是如果你初始默认使用中文&#xff0c;想要切换英文&#xff0c;我们可以切换到英文模式。同时&#x…

UOS中读取 bitlocker加密的U盘

目前安装的1079专业版本&#xff0c;apt 安装的dislocker版本太低&#xff0c;要安个高点的 git clone https://github.com/Aorimn/dislocker sudo apt install gcc cmake make libfuse-dev libmbedtls-dev ruby-dev cd dislocker cmake . make sudo make install 这时运等l…

RHCE笔记-DNS服务器

一.DNS简介 DNS&#xff08;域名系统&#xff09;是一种互联网服务&#xff0c;负责将我们熟悉的域名&#xff08;比如 www.example.com&#xff09;转换为计算机能理解的IP地址&#xff08;比如 192.0.2.1&#xff09;。这样&#xff0c;当你在浏览器中输入网址时&#xff0c;…

自研小程序-心情追忆

在近期从繁忙的工作中暂时抽身之后&#xff0c;我决定利用这段宝贵的时间来保持我的Java技能不致生疏&#xff0c;并通过一个个人项目来探索人工智能的魅力。 我在Hugging Face&#xff08;国内镜像站点&#xff1a;HF-Mirror&#xff09;上发现了一个关于情感分析的练习项目&…

Admin.NET源码学习(5:swagger使用浅析)

直接启动Admin.NET.Web.Entry项目&#xff0c;会弹出swagger登录验证框&#xff0c;虽然采用Furion简化了项目加载过程及配置&#xff0c;但是学习源码过程就比较恼火&#xff0c;很多设置及功能搞不清楚到低是怎么启用的&#xff0c;本文记录学习Admin.NET项目中swagger的设置…

Pytorch学习--神经网络--线性层及其他层

一、正则化层 torch.nn.BatchNorm2d torch.nn.BatchNorm2d(num_features, eps1e-05, momentum0.1, affineTrue, track_running_statsTrue, deviceNone, dtypeNone)正则化的意义&#xff1a; 加速训练收敛&#xff1a;在每一层网络的输入上执行批量归一化可以保持数据的分布稳…

SSH免密钥登录

1: 用 ssh-key-gen 在本地主机上创建公钥和密钥 winr cmd 打开控制台 ssh-keygen -t rsa 一直按enter 2: 用 ssh-copy-id 把公钥复制到远程主机上 user 是用户名 remote_host是远程主机 ssh-copy-id -i ~/.ssh/id_rsa.pub userremote_host 3: 直接登录远程主机&#xf…

推荐一款优秀的pdf编辑器:Ashampoo PDF Pro

Ashampoo PDF Pro是管理和编辑 PDF 文档的完整解决方案。程序拥有您创建、转换、编辑和保护文档所需的一切功能。根据需要可以创建特定大小的文档&#xff0c;跨设备可读&#xff0c;还可以保护文件。现在您还能像编辑Word文档一样编辑PDF! 软件特点 轻松处理文字 如 Microso…

使用C语言实现经典贪吃蛇游戏

一、项目概述 我们的目标是创建一个基本的贪吃蛇游戏&#xff0c;它具有以下功能&#xff1a; 蛇能够根据玩家的键盘输入改变方向。 当蛇吃到食物时&#xff0c;蛇的长度增加&#xff0c;同时分数增加。 蛇可以在窗口边缘“穿墙”移动。 游戏界面包括一个蛇和一个随机出现…

Spring Boot Configuration和AutoConfiguration加载逻辑和加载顺序调整

在spring中&#xff0c; AutoConfiguration也是一个种Configuration&#xff0c;只是AutoConfiguration是不能使用proxy的。 而且spring对于两者的加载顺序也不是一视同仁&#xff0c;是有顺序的。spring会先加载SpringBootApplication可达的且标注了Configuration的类&#x…