设计模式 springboot相对于ssm优点 ThreadLocal 线程和进程 SpringBean生命周期 Get和Post 创建线程 Http中重定向和转发 同步和异步

1.设计模式

观察者模式

原型模式

责任链模式

单例模式

工厂模式

构建者模式

适配器模式策略模式,都是用来解决不同类之间的接口不兼容问题,但是它们的实现方式和应用场景有所不同。

适配器模式(Adapter Pattern)是一种结构型设计模式,用于将一个类的接口转换成客户端所期望的另一种接口。适配器模式通过创建一个包装类来实现接口的转换,这个包装类包含了原始类的实例,并将客户端的请求转发给原始类。适配器模式通常用于现有的类库或组件需要与新的系统进行集成时,因为这些现有的类库或组件的接口通常不符合新系统的要求。

策略模式(Strategy Pattern)是一种行为型设计模式,用于在运行时动态地选择算法或行为。策略模式通过将算法或行为封装在不同的策略类中,使得这些算法或行为可以在运行时被动态地替换或切换。策略模式通常用于需要根据不同的情况使用不同的算法或行为的场景,如排序、搜索、加密等。

总之,适配器模式用于将一个接口转换成另一个接口,而策略模式用于动态地选择算法或行为。两种模式都是用来解决不同类之间的接口不兼容问题,但是它们的实现方式和应用场景有所不同。

2.springboot相对于ssm有什么优点

SpringBoot和SSM(Spring+SpringMVC+MyBatis)都是常用的Java Web开发框架,它们各有优缺点,具体如下:

SpringBoot的优点:

  1. 简化了项目搭建和配置过程,可以快速构建Web应用;
  2. 自带内嵌容器(Tomcat、Jetty等),可以直接打包成可执行Jar包运行;
  3. 自动配置(Auto Configuration)特性,大大减少了重复配置的工作;
  4. 强大的 starter 机制,一键添加依赖并自动配置;
  5. 可以轻松集成其他Spring组件(如Spring Data、Spring Security等);
  6. 能够通过Actuator监控和管理应用程序。

SSM的优点:

  1. SSM框架成熟、稳定,在企业级应用中应用广泛;
  2. 数据访问层(MyBatis)配置灵活,可自定义SQL语句,对于复杂查询更加友好;

综上所述,SpringBoot相对于SSM具有更快的开发效率、更便捷的部署方式、更强大的扩展性和更完善的监控功能,而SSM则在数据访问方面拥有更为灵活的配置和更广泛的实践经验。因此,具体选择哪一个框架应根据项目需求和实际情况来进行权衡。

3.ThreadLocal是什么,有哪些使用场景

ThreadLocal 是 Java 中的一个线程级别的存储工具,是一个线程本地变量,它可以为每一个线程都创建一个私有的变量,每个线程只能获取到自己的变量,从而避免了线程不安全问题。

ThreadLocal 的核心是 ThreadLocalMap 类,它是一个线程级别的哈希表,用于存储每个线程的变量。ThreadLocalMap 以 ThreadLocal 对象作为 key,以变量值作为 value,每个线程都可以通过 ThreadLocalMap 获取自己的变量。

1. 需要保存线程的上下文信息,例如用户信息,通过token解析出来的用户id等

2. 需要对线程的局部变量进行隔离,避免线程不安全问题;

3. 需要在跨类跨方法使用同一个变量,同时又不希望使用全局变量的情况;

4. 需要避免传递参数的繁琐,例如在 Spring 框架中使用的事务管理。

需要注意的是,由于 ThreadLocal 存储数据的副本是存储在每个线程的 ThreadLocalMap 中的,因此需要及时清理 ThreadLocal 中存储的数据,以避免内存泄漏问题。通常可以在使用完毕后通过调用 ThreadLocal 的 remove() 方法来清理数据,或者使用 Java 8 引入的新特性 ThreadLocal.withInitial() 方法来避免使用 remove() 方法。

4.什么是线程和进程

进程:具有一定独立功能的程序,是操作系统进行资源分配和调度的一个独立单位。

线程:进程中执行运 算的最小执行单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。

特点:进程可以创建多个线程,这使进程拥有高并发性能力。进程在运行时各自内存单元相互独立,同一个进程内线程之间内存共享。

(进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程,但一个进程一般有多个线程)

5.SpringBean的生命周期

Bean 的生命周期是指 Bean 对象从创建到销毁的整个过程,一般包含以下几个阶段:

1.实例化: 当容器启动时,会根据配置文件或注解等方式创建 Bean 对象的实例,并将其放入容器中管理。

会对其进行属性赋值,可以通过构造函数注入

2.配置: 客器在创建 Bean 实例后setter 方法注入、自动注入等方式实现。

3.初始化:在 Bean 实例全部属性赋值完成后,容器会调其初始化方法,可以通过@PostConstruct 注解或实现 InitializingBean 接口来定义 Bean 的初始化方法

4.使用: 在 Bean 实例初始化之后,可以通过容器从中获取到 Bean 对象并使用.

5.销毁: 当容器关闭时,会回调 Bean 的销毁方法,可以通过 @PreDestroy 注解或实现DisposableBean 接门来定义 Bean 的销毁方法。

6.Get和Post的区别

GET和POST是HTTP协议中常用的两种请求方法,它们有以下区别:

参数传递方式:

  • GET方法通过URL参数传递数据,将数据附加在URL的末尾
  • POST方法通过请求体传递数据,在HTTP请求头中设置Content-Type为application/x-www-form-urlencoded或multipart/form-data,并将参数以键值对的形式放在请求体中。

数据传输安全性:

  • GET请求的参数在URL中可见,可能会被缓存、浏览器历史记录或代理服务器记录下来,不适合传输敏感信息。
  • POST请求的参数在请求体中,相对于GET请求更安全,适合传输敏感信息。

请求长度限制:

  • GET请求的参数长度通常受限于浏览器或服务器的最大URL长度限制,过长的参数可能会导致请求失败。
  • POST请求的参数长度理论上没有限制,但实际上仍受服务器的限制。

幂等性:

  • GET请求具有幂等性,即多次请求对服务端资源没有影响,不会引起副作用。
  • POST请求不具备幂等性,多次请求可能会对服务端资源造成多次影响。

缓存处理:

  • GET请求通常可以被浏览器缓存,当再次请求相同URL时,浏览器可以直接从缓存中获取数据,节省网络开销。
  • POST请求通常不会被浏览器缓存。

综上所述,GET适合用于获取数据、幂等操作和对访问路径有限制的场景,而POST适合用于提交数据、非幂等操作和对数据安全性要求较高的场景。根据具体的业务需求和安全考虑,选择合适的请求方法。

7.创建线程的常用方式有以下几种

1.继承Thread类并重写run()方法。

2.实现Runnable接口并重写run()方法。将Runnable实例作为Thread类的构造函数参数传递并启动线程。

3.实现Callable接口并重写call()方法。使用ExecutorService的submit()方法提交Callable任务,并通过FutureTask对象获取返回值。

4.使用线程池。线程池可以重复使用线程,提高性能和可靠性。Java提供了Executor框架来管理线程池。常见的线程池实现类包括ThreadPoolExecutor和ScheduledThreadPoolExecutor

8.Http中重定向和转发的区别

转发:用 request 的 getRequestDispatcher()方法得到 ReuqestDispatcher 对象,调用

forward()方法request.getRequestDispatcher("other.jsp").forward(request, response);

重定向:调用 response 的 sendRedirect()方法

response.sendRedirect("other.jsp");

1> 重定向 2 次请求,请求转发 1 次请求

2> 重定向地址栏会变,请求转发地址栏不变

3> 重定向是浏览器跳转,请求转发是服务器跳转

4> 重定向可以跳转到任意网址,请求转发只能跳转当前项目

5>请求转发不会丢失请求数据,重定向会丢失

9.同步和异步的区别

  • 同步:发出一个调用之后,在没有得到结果之前, 该调用就不可以返回,一直等待。
  • 异步:调用在发出之后,不用等待返回结果,该调用直接返回。

10.Spring中的bean是什么时候被实例化

在 Spring 容器启动时,会读取配置文件中的 Bean 定义信息,然后根据 Bean 定义的信息实例化相应的 Bean 对象。具体来说,Bean 的实例化时间与 Bean 的作用域有关:

1. 单例作用域(Singleton):在 Spring 容器启动时,会立即实例化单例作用域的 Bean,将它们存储在容器的 Bean 工厂中,以便随时获取。

2. 原型作用域(Prototype):在请求获取原型作用域的 Bean 时,Spring 容器才会实例化该 Bean,并返回给请求方。

3. 其他作用域:如 Web 作用域和 Session 作用域等,它们的实例化时间依赖于具体的使用场景。

需要注意的是,Bean 的实例化过程可能会涉及到复杂的依赖关系,因此 Spring 容器会根据 Bean 定义信息中的依赖关系,先实例化依赖的 Bean,再实例化当前 Bean。同时,Spring 还支持使用构造方法注入和属性注入两种方式,可以在 Bean 实例化的过程中,将所需的依赖注入到当前 Bean 中。

11.如何理解线程安全和不安全?

线程安全和不安全是在多线程环境下对于同一份数据的访问是否能够保证其正确性和一致性的描述。

  • 线程安全指的是在多线程环境下,对于同一份数据,不管有多少个线程同时访问,都能保证这份数据的正确性和一致性。
  • 线程不安全则表示在多线程环境下,对于同一份数据,多个线程同时访问时可能会导致数据混乱、错误或者丢失。

12.HashMap的底层实现原理

1.哈希算法

HashMap中的key通过调用其hashCode()方法获取一个哈希值(hash code),然后通过取模运算将哈希值映射到数组(table)的一个位置上,并将该位置作为该key值的索引。HashMap默认的大小为16,每当数组中的元素超过了它的容量(load factor * capacity,默认0.75(加载因子) * 16 = 12),则会对数组进行扩容(扩容为之前的两倍),将当前数组中的元素重新散列到新的更大的数组中,以提高存储效率。

2.解决哈希冲突的方法

由于不同的key值可能会映射到数组的相同位置上(发生哈希冲突),因此需要一种方法来解决这个问题。Java中的HashMap使用链表和红黑树两种方式来解决哈希冲突:

  • 当数组长度大于64,链表长度达到阈值(默认8)时,链表会自动转换为红黑树,以提高查找效率。
  • 当其节点降低为6时,红黑树转化为链表

14.什么是内存溢出和内存泄露

内存溢出(Out of Memory Error)指的是当JVM中的可用内存不足以满足程序运行所需时,抛出的异常。这种情况通常发生在创建了大量对象并长时间持有时,导致无法为新的对象分配内存空间而抛出异常。内存溢出也可能由于某些原因导致系统资源短缺,如线程过多、大数据量等。

内存泄漏(Memory Leak)是指程序运行过程中,由于无法释放不再使用的内存,导致可用内存越来越少,最终导致系统崩溃或变得非常缓慢的一种现象。典型的内存泄漏情况是当程序使用完对象后没有将其及时清除,使得这些对象不能被GC回收,随着时间的推移,会导致内存越来越小,直到耗尽。

它们的根本原因不同:内存溢出是因为程序需要的内存超过了系统能提供给它的内存,而内存泄漏则是因为程序一直占用着不需要的内存而导致的内存泄漏。解决内存溢出可以考虑增加系统内存或者优化代码以节省内存,而解决内存泄漏需要找到并修复程序持有不必要对象的BUG。

15.volatile 关键字的作用

作用

说明

保证变量的可见性

当一个线程修改了一个 volatile 变量的值时,在下一次访问这个变量之前,会将该变量的最新值从主内存中读取,以确保所有线程都能看到最新的值。

禁止指令重排

使用 volatile 关键字可以禁止指令重排优化,保证指令的有序执行

不保证原子性

单独使用 volatile 关键字不能保证操作的原子性,如果需要保证原子性,需要使用其他的同步机制,如 synchronized 或者使用原子类(Atomic 类)。

16.单例模式中的DCL机制

DCL(Double-Checked Locking)是一种在多线程环境下实现单例模式的优化方案。该方案利用了synchronized关键字的互斥性和volatile关键字的内存可见性,既确保了线程安全,又能够在一定程度上提高单例对象获取的效率。

具体来说,DCL机制的单例模式实现步骤如下:

  1. 声明一个静态的、volatile修饰的单例对象变量,确保其在多线程环境下的内存可见性。
  2. 定义一个私有的无参构造方法,防止外部直接创建新对象。
  3. 定义一个静态的获取单例对象的方法,首先对单例对象进行判空操作,如果为空就通过synchronized关键字的互斥性保证只有一个线程进入临界区创建新的单例对象,然后再次判空并将新创建的单例对象赋给单例对象变量,最后返回该单例对象。

需要注意的是,DCL机制虽然可以提高单例对象获取的效率,但由于JVM的指令重排优化,可能会导致在多线程环境下出现问题,因此需要使用volatile关键字来避免该问题。

17.spring security大概流程

Spring Security是一种基于Spring框架的安全性认证和授权框架,它提供了一系列开箱即用的安全性特性,包括认证、授权、攻击防护等功能,可以帮助应用程序更加容易地实现各种安全需求和权限控制。

Spring Security的大概流程如下:

  1. 用户访问受保护的资源,如请求访问web应用的某个URL。
  2. Spring Security的过滤器链(filter chain)对请求进行预处理,根据配置确定是否需要进行安全性检查,如进行token认证。
  3. 从token中解析出用户的角色信息。
  4. 在接口中添加相应的权限注解,可以根据token中的角色信息进行权限验证。

需要注意的是,在上述过程中,Spring Security并不仅仅是一个简单的过滤器或拦截器,而是一个完整的安全性框架,它提供了丰富的功能和灵活的配置方式,允许开发者自定义各种安全策略来满足具体的需求。

19.docker logs发现账号没有权限怎么办

docker privileged

20.Synchronized的原理和其特点

原理:Synchronized 的原理是,它会使用对象的内置锁(也称为监视器锁)来实现同步,每个对象都有一把内置锁,当一个线程访问一个同步代码块时,它会尝试获取这个锁,如果锁被其他线程持有,则该线程将被阻塞,直到锁被释放

特点:

1. 互斥性:Synchronized 保证同一时刻只有一个线程可以获取锁,并且只有该线程可以执行同步代码块中的代码。

2. 可重入性:同一个线程可以多次获取同步锁而不会被阻塞,这样可以避免死锁的发生。

3. 独占性: 如果一个线程获得了对象的锁,则其他线程必须等待

该线程释放锁之后才能获取锁。

4.缺点:非公平锁 ,当锁被释放后,任何一个线程都有机会竞争得到锁,这样做的目的是提高效率,但缺点是可能产生线程饥饿现象

21.Bitmap的特性

  1. 紧凑存储:Bitmaps以非常紧凑的方式存储位信息。Redis使用bit字段来保存位图,每个bit只占用一个二进制位(0或1),因此可以有效地节省存储空间。
  2. 高效操作:Redis提供了一系列高效的位操作命令来操作Bitmaps。这些命令可以对位图进行逻辑运算(与、或、异或、非)、统计位的数量、获取指定位的值等操作,这些操作可以在O(1)的时间复杂度内完成。
  3. 合操作:由于Bitmaps实际上是由位组成的集合,因此可以对Bitmaps执行集合操作。例如,可以对两个位图执行并集、交集、差集等操作,从而方便地进行数据分析和处理。
  4. 空间效率:Bitmaps在处理大量数据时非常高效。由于位操作是基于二进制位的,所以可以用相对较少的存储空间表示和操作大规模的数据集。
  5. 计数功能:通过位操作命令,可以对位图中设置为1的位进行计数。这对于跟踪用户活动、统计在线用户、计算UV(Unique Visitors)等场景非常有用。
  6. 可与其他数据结构结合使用:Bitmaps可以与其他Redis数据结构结合使用,例如使用位图对某个键进行标记,然后将该键添加到集合、列表、哈希等其他数据结构中进行更复杂的操作。

Bitmaps在Redis中被广泛应用于各种场景,如大规模数据集的统计分析、布隆过滤器的实现、用户行为跟踪和推荐系统等。其高效的位操作和紧凑的存储特性使得Bitmaps成为处理大规模二进制数据的理想选择。

22.你知道reflect包吗?它提供了哪些功能?

在Java中,java.lang.reflect包提供了与反射相关的功能。主要功能包括以下几个方面:

  1. 获取和操作类的信息:通过Class类可以获取类的信息,包括类名、访问修饰符、父类、接口、字段、方法等。可以通过反射创建对象、获取和设置字段的值,以及调用方法等。
  2. 创建对象:通过反射可以根据类的信息创建对象实例。使用Class.newInstance()方法可以创建一个类的新实例,前提是该类具有一个公共的无参数构造函数。另外,还可以通过Constructor类获取构造函数,并使用newInstance()方法创建对象实例。
  3. 获取和设置字段的值:通过反射可以获取类的字段信息,并对字段进行读取和设置操作。可以使用Field类获取字段对象,并通过get()和set()方法获取和设置字段的值。
  4. 调用方法:通过反射可以动态调用类的方法。可以使用Method类获取方法对象,并通过invoke()方法调用方法,同时也可以传递方法的参数和获取返回值。
  5. 操作数组:通过反射可以操作数组对象。可以使用Array类对数组进行动态创建、赋值、获取长度等操作。
  6. 判断类型信息:通过反射可以判断对象的类型和类之间的关系。可以使用instanceof关键字来判断对象是否为某个类型的实例,也可以使用isAssignableFrom()方法判断一个类是否为另一个类的父类或接口。
  7. 泛型类型信息:反射还提供了对泛型类型信息的支持。可以通过ParameterizedType类获取泛型类型的参数信息,例如获取泛型类的类型参数、获取方法的参数化类型等。

需要注意的是,反射的使用会带来一定的性能开销,并且可能引入不安全的操作。因此,在正常情况下,应该谨慎使用反射,避免滥用。反射通常在框架、工具和动态编程等场景下使用,以实现灵活的功能和扩展性。

23.你认为怎么样算的上是一个中级开发?

  1. 扎实的编程基础:对编程语言(比如Java、Python等)有较深入的理解,熟悉常用的数据结构和算法,并能够独立完成简单到中等难度的编码任务。
  2. 熟悉常用的开发工具和框架:对于开发领域常用的IDE(集成开发环境)和版本管理工具(如Git)有一定的使用经验,了解常用的开发框架(如Spring、Django等)并能够灵活运用。
  3. 代码质量和可读性:写出结构清晰、可扩展性强、维护性好的代码,并遵循良好的编程风格和规范。能够使用单元测试、代码审查等工具和方法提高代码质量。
  4. 解决问题的能力:具备分析和解决问题的能力,能够独立思考和找出合适的解决方案。善于查找文档、资料和社区等资源,并能够快速学习和消化新知识。
  5. 设计和架构能力:具备基本的软件设计和架构能力,能够根据需求设计合适的系统结构,并考虑性能、安全性、可扩展性等方面的因素。
  6. 团队协作和沟通能力:能够与团队成员积极沟通、合作,理解和遵守团队的开发规范和流程,参与需求讨论和团队协作,有一定的项目经验和开发流程的了解。
  7. 持续学习和自我提升的意愿:拥有持续学习的态度和能力,关注技术的发展和趋势,不断提升自己的技术水平和专业知识。

请注意,以上只是列举了一些中级开发的典型特点和技能要求,并非详尽无遗。实际上,评判一个中级开发还需要综合考量项目经验、工作表现、解决问题的能力等多个方面因素。

24.创建对象的方式有哪些?

  • 1、new关键字
  • 2、Class.newInstance 反射机制

(自Java 9版本起,推荐使用 Constructor.newInstance() 方法来创建对象,因为 Class.newInstance() 方法已被废弃。)

  • 3、Constructor.newInstance 反射机制通过构造函数创建对象的实例
  • 4、Clone
  • 5、反序列化

25.你们里gateway 干啥了,具体是如何实现的

1.token验证

2.接口耗时统计

3.全链路跟踪

26.final 在 Java 中有什么作用

  • final 修饰的类叫最终类,该类不能被继承。
  • final 修饰的方法不能被重写。
  • final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。

27.你们是怎么保证消息不丢失了?

可以使用MQ的生产者确认机制

死信队列

29.你了解的RPC框架有哪些?

RPC框架(Remote Procedure Call)是一种用于构建分布式应用程序的通信协议。它允许不同的进程(可以在不同的机器上)通过网络进行通信,像本地调用一样调用远程服务。RPC框架的主要目的是隐藏远程调用的复杂性,使得开发人员可以像调用本地函数一样调用远程函数,从而简化分布式系统的开发。常见的RPC框架有gRPC、Apache Thrift、Dubbo、Spring Clould等。

31.spring如何处理并发问题

Spring 框架本身并不提供处理线程并发问题的解决方案,但可以借助 Java 多线程相关的 API 或第三方库来解决此类问题。

在 Spring 中,可以使用线程池来管理线程,避免创建过多的线程导致系统资源浪费和性能下降。Spring 提供了 TaskExecutor 接口和 ThreadPoolTaskExecutor 实现类,可以方便地使用线程池来执行异步任务或并发任务。

例如,可以使用@Async注解和@Scheduled注解来执行异步任务和定时任务,Spring 将自动使用线程池来执行这些任务,避免阻塞主线程。同时,Spring 还提供了一系列的并发编程JUC包工具类,例如 CountDownLatch、CyclicBarrier、Semaphore 等,可以帮助开发者更好地管理并发任务。

除此之外,Spring 还可以与其他第三方库一起使用来解决线程并发问题。例如,使用 Spring 和 Redis 可以实现分布式锁,使用 Spring 和 RabbitMQ 可以实现异步消息处理等。

总之,Spring 框架本身并不提供处理线程并发问题的解决方案,但是可以借助 Java 多线程相关的 API 或第三方库来解决此类问题,并通过 Spring 提供的 TaskExecutor 接口和 ThreadPoolTaskExecutor 实现类来管理线程池。

32.线程池的主要参数

参数

说明

核心线程数

线程池中始终保持存活的线程数量。在创建线程池时就会初始化这些线程,即使没有任务要执行,它们也会一直存在。

最大线程数

线程池中允许存在的最大线程数量。当任务队列已满且线程数未达到最大线程数时,会创建新的线程来执行任务。

任务队列

保存待执行任务的队列。当线程池中的线程都在执行任务且任务队列已满时,新的任务将被暂时放入队列,并等待线程池中的线程可用。

非核心线程存活时间

当线程池中的线程数量超过核心线程数时,空闲的非核心线程在经过一定时间后会被回收销毁,从而减少资源消耗。

拒绝策略

当任务队列已满且线程池中的线程数达到最大线程数时,新的任务无法被执行时,采取的策略。常见的策略有直接抛出异常、丢弃任务、丢弃最旧的任务和由调用者所在的线程执行

33.并行和并发有什么区别

并行和并发是计算机领域中常用的概念,两个术语都涉及同时执行多个任务的能力,但它们的实现方式略有不同。

并行是指同时执行多个任务,例如在多个处理器上同时处理多个任务。每个任务都是独立的,并且可以并行运行,从而提高系统的吞吐量。如果每个处理器都在独立执行自己的任务,那么就实现了并行。

举个例子,假设我们有一个计算机集群,其中有10个处理器。如果我们将一个程序分成10个独立的任务,并将每个任务分配给一个处理器,则每个处理器都可以独立执行自己的任务。这就是并行的一个例子。

并发是指在一段时间内同时处理多个任务,注意这里是“同时处理”而不是“同时执行”。这是因为在处理器中只有一个处理器内核,而同时执行多个任务是不可能的。因此,处理器通过轮流处理不同的任务,在不同任务之间切换,从而实现并发处理。

举个例子,假设我们有一个web服务器,收到了多个客户端的请求。这些请求需要在服务器上处理,但是由于服务器只有一个处理器内核,因此服务器必须轮流处理不同的请求,这样看起来就像是所有请求都同时在处理。这就是并发的一个例子。

总之, 并行处理是在多个处理器上同时执行多个任务,而并发处理是在一个处理器内核上轮流处理多个任务。

34.String str="i"与 String str =new String("i")一样吗

不一样,因为内存的分配方式不一样。String str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String("i") 则会被分到堆内存中。

35.什么是线程死锁?死锁如何产生?如何避免线程死锁?

例如,假设线程 A 持有资源 X,并等待资源 Y,而线程 B 持有资源 Y,并等待资源 X,这时候就会出现死锁。

死锁

线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。

当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块。

当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。

死锁的产生的一些特定条件:

Java 中,死锁产生的一些特定条件通常包括以下四个方面:

1. 互斥条件:一个资源一次只能被一个线程持有,如果其他线程想要获取该资源,就必须等待该线程释放该资源。

2. 保持条件:一个线程请求资源时,如果已经持有了其他资源,就可以保持对这些资源的控制,直到满足所有资源的要求才释放。

3. 不剥夺条件:已经分配的资源不能被其他线程剥夺,只能由持有资源的线程释放。

4. 环路等待条件:多个线程形成一种循环等待的关系,每个线程都在等待其他线程所持有的资源,从而导致死锁的产生。

当以上条件同时满足时,就可能会出现死锁的情况。为了避免死锁,需要在设计时遵循一定的规范和原则,例如尽量避免嵌套锁,确保同步代码块执行时间尽可能短等,同时也可以使用专门的工具进行死锁检测和分析,帮助我们找到死锁的根本原因并进行相应的优化和调整。

如何避免

要避免线程死锁,可以采取以下几种方法:

1. 尽量避免使用多个锁,尽量使用一个锁或者使用更加高级的锁,例如读写锁或者 ReentrantLock。

2. 确保同步代码块的执行时间尽可能短,这样可以减少线程等待时间,从而避免死锁的产生。

3. 使用尝试锁,通过 ReentrantLock.tryLock() 方法可以尝试获取锁,如果在规定时间内获取不到锁,则放弃锁。

4. 避免嵌套锁,如果需要使用多个锁,请确保它们的获取顺序是一致的,这样可以避免死锁。

36.http和https的区别

1.安全性不同

http协议:是超文本传输协议,信息是明文传输。如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息。

https协议:HTTPS通过使用SSL/TLS加密技术,可以确保数据传输的安全性,更加健壮安全。

2.连接方式不同

HTTP协议是基于TCP协议之上的,在发送请求和接收响应之后就会断开连接。

https协议:HTTP协议的基础之上,使用了SSL/TLS协议,通过握手过程建立了一条加密通道,通信双方可以在通道上进行长时间的、安全的数据传输。

3.端口不同

http协议:使用的端口是80。

https协议:使用的端口是443.

4.证书申请方式不同

http协议:无证书。

https协议:需要到ca申请证书,一般免费证书很少,需要交费。

37.接口和抽象类有什么区别

  • 实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。
  • 构造函数:抽象类可以有构造函数;接口不能有。
  • 实现数量:类可以实现很多个接口;但是只能继承一个抽象类。
  • 访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符。

38.Collection 和 Collections 有什么区别

  • Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如 List、Set 等。
  • Collections 是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如提供的排序方法:Collections. sort(list)。

39.深拷贝与浅拷贝的区别

深拷贝将对象的全部内容重新拷贝,得到一个新的对象

浅拷贝只拷贝原始引用,不拷贝引用对象

他们的区别主要是是否拷贝了引用对象

40.ArrayList 和 LinkedList 的区别是什么

  • 数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。
  • 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
  • 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。
  • 综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList

41.HashMap和HashTable两者区别

1. 线程安全性:Hashtable是线程安全的,而HashMap则不是。Hashtable的方法都是同步的,可以在多线程环境下使用,但在性能上会受到一定的影响。HashMap在多线程环境下需要额外的同步措施来保证线程安全。

2. null值:HashMap允许键和值都为null,而Hashtable不允许。在HashMap中,可以有一个键为null的键值对,以及多个值为null的键值对。而在Hashtable中,如果键或值为null,会抛出NullPointerException。 3. 继承关系:Hashtable是基于Dictionary类的,而HashMap是基于AbstractMap类的。由于Dictionary类是一个过时的类,不推荐使用,所以在新的代码中建议使用HashMap。 4. 迭代器:Hashtable的迭代器是通过Enumeration实现的,而HashMap的迭代器是通过Iterator实现的。Iterator比Enumeration更加强大和灵活,因此HashMap的迭代器支持更多的操作。 5. 性能:由于Hashtable是线程安全的,它的性能通常会比HashMap差一些。在单线程环境下,HashMap的性能更好,因为它不需要进行额外的同步操作。 综上所述,如果不需要考虑线程安全性,并且希望有更好的性能和灵活性,推荐使用HashMap。如果需要线程安全性,可以使用Hashtable,但需要注意性能方面的影响

42.Spring框架中哪些地方使用了反射?

在 Spring 框架中,反射机制被广泛用于以下几个方面:

1. 依赖注入:Spring 使用反射机制获取对象并进行属性注入,从而实现依赖注入。

2. AOP:Spring AOP 使用 JDK 动态代理或者 CGLIB 字节码增强技术来实现 AOP 的切面逻辑,这其中就包含了对被代理对象方法的反射调用。

3. MVC 框架:Spring MVC 框架使用反射来调用相应的控制器方法,从而实现请求的处理。

4. 数据库访问框架:Spring 的 JDBC 框架使用反射机制来实现对数据库的访问。

5. 容器管理:Spring 容器也使用了反射机制来管理对象的实例化和依赖注入。

需要注意的是,虽然反射机制为开发者提供了极大的便利性,但是过度使用反射也可能导致性能问题,在使用时需要进行适量控制。

43.Atomic 原子类

原子类都存放在java.util.concurrent.atomic下

基本类型

使用原子的方式更新基本类型

  • AtomicInteger:整型原子类
  • AtomicLong:长整型原子类
  • AtomicBoolean:布尔型原子类

数组类型

使用原子的方式更新数组里的某个元素

  • AtomicIntegerArray:整型数组原子类
  • AtomicLongArray:长整型数组原子类
  • AtomicReferenceArray:引用类型数组原子类

AtomicInteger 类主要利用 CAS (compare and swap) + volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。

44.Files的常用方法都有哪些?

  • Files. exists():检测文件路径是否存在。
  • Files. createFile():创建文件。
  • Files. createDirectory():创建文件夹。
  • Files. delete():删除一个文件或目录。
  • Files. copy():复制文件。
  • Files. move():移动文件。
  • Files. size():查看文件个数。
  • Files. read():读取文件。
  • Files. write():写入文件。

45.stream中常用的操作方法

1.过滤:使用filter()方法可以过滤掉集合中不符合条件的元素。

2.映射:使用map()方法可以对集合中的每一个元素进行映射处理。

3.排序:使用sorted()方法可以对集合中的元素进行排序。

4.去重:使用distinct()方法去掉集合中的重复的元素。

5.统计:使用count()方法可以对集合中的元素进行统计。

6.聚合:使用reduce()方法可以对集合中的元素进行聚合计算。

7.遍历:使用forEach()方法可以遍历集合中的每一个元素。

8.匹配:使用anyMatch()、allMatch()、noneMatch()方法可以对集合中的元素进行匹配判断。

9.分组:使用groupingBy()方法可以按照某一个属性进行分组。

10.转换:使用collect()方法可以将集合中的元素转换为另一个集合。

11.平均:使用average()方法可以用于计算一组元素的平均值。

47.Array 和 ArrayList 有何区别

  • Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。
  • Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。
  • Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。

48.Redis是单线程还是多线程

1、Redis5及之前是单线程版本

2、Redis6开始引入多线程版本(实际上是 单线程+多线程版本)

Redis5及之前的版本使用的是 单线程,也就是说只有一个 worker队列,所有的读写操作都要在这一个队列进行操作,好处是不会有线程安全问题(因为它在读写时就只有一个线程,那对于读写操作肯定没有线程安全问题啊!),但是读写 write、read 这些系统调用在Redis执行期间占用了大部分的 CPU 时间,所以这就是单线程模式的缺点。

Redis6引入了多线程机制,但是不是说有多个worker线程同时并发读写,而是它有 “一个 worker线程+多个IO子线程”,其实就是在 IO 就绪之后使用多线程提升读写解析数据的效率,而在操作内存数据的时候还是用单线程。

利用这种单线程+多线程共同 运作的机制,将CPU的性能显著提升了。

同时,这种机制同样不会产生线程安全问题,因为Redis在针对数据的内存操作时,是在一个公共的worker队列中实现的,先进先出,所以不会有线程安全问题。

Redis6之所以保留worker单主线程是因为单线程机制使得Redis内部实现的复杂度大大降低,而且可以保证操作的线程安全。(如果整个过程全让子线程做了,整个任务处理过程太重,就失去了原来单线程高效处理的优势了)

简单来说,就是 “请求是多线程的,但核心的内存读写操作(或者说读写计算)仍然是单线程的”。

50.Spring AOP 优点

1. 重用代码:AOP 允许在多个类或方法中共享代码。例如,可以使用 AOP 来访问日志记录或安全性检查的基本代码。

2. 简化代码:AOP 的主要目的是将横切关注点与业务逻辑分离。这意味着业务代码将更容易理解和维护。

3. 封装切面逻辑:AOP 允许开发人员将切面逻辑封装到一个模块中,并将其应用到整个应用程序中。这使得切面逻辑更容易维护和测试。

4. 提高性能:AOP 的另一个好处是可以提高应用程序的性能。例如,可以使用 AOP 来缓存数据库查询或缓存 Web 服务调用的结果。

5. 促进松耦合:AOP 使得组件之间的依赖关系更加松散。这使得代码更具可重用性,便于进行单元测试和集成测试。

51.说下你对Spring IOC 的理解

1. Spring IOC是一个管理对象之间依赖关系的容器,它实现了依赖注入技术,可以解决传统的紧耦合问题,降低了项目维护难度。

2. Spring IOC将对象之间的依赖关系交由容器来管理对象,开发者只需要告诉容器需要注入的对象和属性即可,实现了低耦合的开发方式。

3. Spring IOC的核心在于控制反转,即将原本由开发者决定的对象创建、生命周期和属性赋值等交由容器处理,也就是由容器控制应用程序控制流程,提高了代码的可读性和可维护性。

4. Spring IOC的依赖注入实现方式有构造器注入、Setter注入、属性注入等,不仅能够进行对象之间的依赖注入,还可以注入第三方或自定义对象,使得开发者的代码更加简洁易读。

5. Spring IOC的作用不止是解决传统的紧耦合问题,还可以对外部资源进行管理,如数据库连接池、事务管理等,提高了系统的可扩展性和可重用性,(松耦合

52.Spring 注册Bean的方式有哪些?

  • 基于注解:使用注解方式可以非常简洁地注册Bean,常用的注解有@Component、@Service、@Repository和@Controller等。通过在类上添加相应的注解,Spring会自动扫描并注册这些Bean。
  • XML配置:使用XML文件来配置Bean的注册信息,可以通过在XML文件中使用<bean>标签来定义Bean的属性,并使用<bean>标签的id属性指定Bean的名称。
  • Java配置类:使用Java配置类来注册Bean,通过在Java配置类中使用@Configuration注解来声明配置类,并在配置类中使用@Bean注解来定义Bean的属性和实例化方式。
  • 手动注册:可以通过编程方式手动注册Bean,即使用ApplicationContext接口提供的方法,如registerBeanDefinition()或者registerSingleton()来注册Bean。

53.开发一个微服务应用会用到哪些系统架构

开发一个微服务应用时,通常会用到以下几种系统架构: 1. 微服务架构:微服务架构将应用程序拆分为一组小型、独立的服务,每个服务都有自己的业务逻辑和数据库。这种架构使得开发团队可以独立开发、测试、部署和扩展每个服务,从而提高开发效率和系统的可伸缩性。 2. 服务注册与发现(nacos):微服务架构中的各个服务需要能够相互发现和通信。为此,可以使用服务注册与发现系统,如Consul、Eureka或ZooKeeper。这些系统允许服务在启动时将自己注册到注册中心,并能够查询和发现其他服务的位置和状态。 3. 负载均衡(ribbon):由于微服务应用通常会有多个实例运行,需要一个负载均衡器来分发请求到各个实例上。负载均衡器可以根据不同的算法(如轮询、随机等)将请求分发到可用的服务实例,以实现负载均衡和高可用性。 4. API网关(gateway):API网关作为微服务应用的入口,负责接收和转发外部请求。它可以处理身份验证、请求路由、请求转换等功能,同时还可以进行请求限流、缓存等操作,以提高系统的性能和安全性。 5. 分布式数据管理:微服务应用中的数据通常会分布在不同的服务中,因此需要一种分布式数据管理机制来保证数据的一致性和可用性。常见的解决方案包括分布式数据库(如MySQL集群、MongoDB副本集等)和分布式缓存(如Redis、Memcached等)。

6.微服务调用组件(OpenFeign):OpenFeign是Spring Cloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并且低耦合调用其他服务。

54.Undo_Log、Redo_Log和Read_Log的作用

在MySQL中,undo log(回滚日志)和redo log(重做日志)是用于实现事务的一致性和持久性的重要组成部分。这两种日志记录了对数据库的修改操作,以确保事务的原子性和持久性。

1. Undo Log(回滚日志):

- Undo log用于回滚事务的操作,即撤销对数据库的修改。当事务回滚或发生崩溃时,MySQL使用undo log将已提交的事务的修改操作逆向执行,恢复到事务开始之前的状态。

- Undo log记录了事务执行期间对数据的旧值,用于回滚和读取一致性读(consistent read)操作。

2. Redo Log(重做日志):

- Redo log用于重做事务的操作,即将已提交的事务的修改重新应用到数据库中。当数据库崩溃或断电后重新启动时,MySQL使用redo log将未持久化的事务操作重新执行,以保证数据的一致性。

- Redo log记录了事务执行期间对数据进行的修改,用于恢复数据库到事务提交后的状态。

3. Read Log(读取日志):

- Read Log是用于实现数据库的复制、备份和负载均衡的一种日志文件。它记录了数据库上的读取操作,以便在多个节点之间同步数据和协调读取请求。

以下是Read Log的一些主要作用和功能:

  1. 数据库复制备份:通过记录读取操作,Read Log可以用于在不同节点之间复制数据库,并保持节点上的数据完全一致。当一个节点对数据库进行写入操作时,它会将写入操作记录到Redo Log中并应用到本地数据上。而其他节点会通过复制Redo Log的方式同步这些写入操作,从而达到数据复制的目的。Read Log的使用可以确保复制和备份的数据一致性。
  2. 负载均衡:在分布式数据库系统中,如果有多个节点提供读取服务,那么如何合理地分配读取请求就成为一个挑战。Read Log记录了读取操作的信息,可以根据该信息来决定将读取请求分发到哪个节点上。通过负载均衡策略,可以使得读取请求在各个节点之间均匀分布,提高系统的并发性能和可扩展性。
  3. 数据一致性:当数据库节点发生故障或恢复时,Read Log可以用于验证数据的一致性。在恢复过程中,系统可以根据Read Log中记录的读取操作,与其他节点进行数据比对,确保数据的一致性。这在故障恢复和数据完整性验证方面起着重要作用。

需要注意的是,Read Log的内容通常是短暂的,不会像Undo Log和Redo Log那样长期持久化,因为它主要用于支持数据库的复制、备份和负载均衡等功能。同时,实际数据库系统中的Read Log的具体实现和使用方式可能会有所差异,取决于数据库管理系统的设计和架构。

-Read Log对于数据库的复制、备份和负载均衡等功能非常重要。它记录了读取操作,帮助保持数据一致性,并为分布式数据库系统提供了负载均衡和数据同步的基础。

总结:

Undo log用于回滚事务的操作,Redo log用于重做事务的操作,而Read log则用于加速一致性读操作的性能。这些日志类型共同作用,确保了MySQL事务的一致性和持久性。

56.servlet的生命周期

servlet的生命周期是指从servlet被创建到被销毁的整个过程

1.加载阶段:容器通过类加载器使用servlet类对应的文件来加载servlet

2.创建阶段:通过调用servlet的构造函数来创建一个servlet实例

3.初始化阶段:通过init()方法来完成初始化工作

4.处理客户请求阶段:在servlet被创建并初始化之后,容器会为每5个来自客户端的请求创建一个代表http请求的servletRequest对象和一个代表http响应的servletResponse对象,然后将他们

作为参数传递给servlet的service()方法。service()方法根据请求的类型(get,post等)调doGet()或者doPsot()等方法进行处理。

get请求没有请求体,请求不安全。

post请求有请求体,适合大规模的数据传送,请求相对安全。

5.销毁阶段:档servlet的生命周期即将结束时,容器调用servlet的destory()方法,释放资源,完成销毁。

57.什么是红黑树

红黑树(Red-Black Tree)是一种自平衡的二叉搜索树,它在插入、删除等操作时通过自动调整来维持树的平衡,从而保证了较为稳定的查找、插入和删除性能。

红黑树具有以下特点:

  1. 每个节点都有一个颜色属性,红色或黑色。
  2. 根节点是黑色的。
  3. 所有叶子节点(NIL节点,空节点)是黑色的。
  4. 如果一个节点是红色的,则其两个子节点都是黑色的。
  5. 任意一条从任意节点到其每个叶子节点的路径上,经过的黑色节点数量相同。

由于这些特性,红黑树具有以下优势:

  1. 搜索、插入和删除操作的最坏情况时间复杂度为O(logN),保证了较高的效率。
  2. 自平衡特性使得红黑树相对于其他平衡树结构更加稳定,适用于动态数据集合,如数据库索引等。

红黑树的自平衡是通过旋转和重新着色来实现的。在进行插入和删除操作时,根据特定规则进行调整,保持树的平衡状态,使得树的高度相对较低,减少了操作的时间复杂度。

59.线程的run()方法和start()方法的区别

1.调用 start() 方法是用来启动线程的,轮到该线程执行时,会自动调用 run();直接调用 run() 方法,无法达到启动多线程的目的,相当于主线程线性执行 Thread 对象的 run() 方法。

2.一个线程对线的 start() 方法只能调用一次,多次调用会抛出 java.lang.IllegalThreadStateException 异常;run() 方法没有限制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值