1、过滤器和拦截器的区别
请求顺序不同:请求先到达过滤器再到拦截器(在servlet里)
所属不同:过滤器是JavaWeb的,拦截器是Spring的
拦截的请求:filter过滤所有的请求,每次请求都会进行过滤,拦截器可以拦截指定的请求
2、常见的HTTP状态码
200:成功
301:重定向,表示资源已永久移动到新的位置,客户端应该更新书签或链接
302:转发,表示资源暂时从不同的 URI 响应,客户端不应该更改书签或链接
400:客户端请求的语法错误,服务器无法理解(400往上的基本上都是客户端的问题,跟url和里面参数有关)
404:服务器无法根据客户端的请求找到资源(网页)。一般来说就是url写的不对或者url里面参数有问题
500:服务器内部报错,无法完成请求(500往上的基本上都是服务器的问题,就跟我们后端编写的代码有关了)
3、cookie和session的区别
客户端第一次向服务端发送请求的时候,服务器在自己内部生成一个session对象,并产生一个cookie,cookie里携带了一个JSESSIONID,session里保存了一个与JSESSIONID有关的id用于下次请求再来的时候识别,接下来把cookie给到客户端,客户端下次再发送请求的时候就会把cookie携带过去,然后用cookie里存的JSESSIONID与session里的id进行关联对比;以此来判断是否同一用户
注意cookie是服务器生成的然后传给客户端的
cookie:
存储位置:存储在客户端
存储内容:只能存储字符串
存储大小:大小有限制,只能存储4kb
安全性:不安全,可能会有CSRF攻击的风险
生命周期:cookie可以主动设置生命周期。还可以通过浏览器工具清除
Cookie的最佳实践:使用Cookie保存少量的不重要的数据,比如:浏览记录,用户名
session:
HttpSession对象是由服务端创建的对象,占据服务器的一小块内存空间,每个浏览器始终对应一个服务器中的session
存储位置: session服务端创建的,保存在服务端,占用服务端内存
存储内容:对象,存啥都可以
存储大小:无限制,取决于服务器内存
安全性:相较于cookie更加安全
生命周期:从创建开始计时如在30min内没有访问session,那么session生命周期就被销毁。
为什么不建议cookie过大
我们cookie要放在浏览器中每次访问传输到服务端,不宜过大,不然每次请求传递的cookie很大不合适;
4、SpringIOC和SpringAOP
SpringIOC(控制反转):
IOC是一个容器,Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系通过 IoC,对象之间的依赖关系由容器在运行时动态地注入,而不是由程序代码静态地指定。这种方式提高了代码的灵活性和可测试性并且将代码解耦。
IOC的实现方式:XML配置、注解配置
SpringAOP(面向切面编程):
AOP基本概念:
- 切面(Aspect):切面是一个模块化的横切关注点。它包含一个或多个通知(Advice)和一个切入点(Pointcut)。
- 通知(Advice):通知定义了切面在特定的连接点(Join Point)上要执行的动作。通知类型包括:
- 前置通知(Before Advice):在方法调用之前执行。
- 后置通知(After Advice):在方法调用之后执行,无论方法是否抛出异常。
- 返回通知(After Returning Advice):在方法成功返回之后执行。
- 异常通知(After Throwing Advice):在方法抛出异常之后执行。
- 环绕通知(Around Advice):在方法调用之前和之后执行,可以控制方法的执行流程。
- 切点(Pointcut):切入点定义了通知应该在哪些连接点上应用。连接点是程序执行过程中的某个点,如方法调用、异常抛出等。
实现AOP的方式:注解配置以及XML文件配置,我们一般使用注解配置
注解配置步骤:
- 定义切面:使用
@Aspect
注解标记一个类为切面。 - 定义通知:使用
@Before
、@After
、@AfterReturning
、@AfterThrowing
和@Around
注解定义通知。 - 定义切入点:使用
@Pointcut
注解定义切入点。
例子:
@Aspect
@Component
@Slf4j
public class PageXAop {
// 定义切入点
@Pointcut("@annotation(cn.by.wms.annotation.PageX)")
public void point() {}
// 环绕通知
@Around("point()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
log.debug("环绕前");
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
String pageNum = request.getParameter("pageNum");
String pageSize = request.getParameter("pageSize");
// 设置分页参数
if(ObjectUtil.isNotEmpty(pageNum) && ObjectUtil.isNotEmpty(pageSize)){
PageHelper.startPage(Integer.valueOf(pageNum), Integer.valueOf(pageSize));
}
Object result = pjp.proceed();
log.debug("环绕后");
return result;
}
}
@Pointcut("@annotation(cn.by.wms.annotation.PageX)"):这意味着任何使用了 @PageX
注解的方法都会被此切入点所匹配
这一段的意思是
- 匹配所有返回类型:
*
表示返回类型可以是任意类型。 - 匹配
com.example.service
包下的所有类:com.example.service.*
表示包名为com.example.service
的所有类。 - 匹配所有方法名:
*
表示方法名可以是任意名称。 - 匹配任意参数列表:
(..)
表示方法可以有任意数量和类型的参数。
5、spring事务
编程式事务
@Service
public class UserService {
@Autowired
private UserDao userDao;
//1.注入事务管理器
@Autowired
private PlatformTransactionManager transactionManager;
//2.编程式事务
public void createUser(User user) {
//3.定义一个事务默认配置
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
//设置事务传播属性
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
//设置事务传隔离级别
def.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
//4.开启事务
TransactionStatus transaction = transactionManager.getTransaction(def);
try {
//保存当前执行操作
userDao.save(user);
// int i = 1 / 0;
//5.提交事务
transactionManager.commit(transaction);
} catch (Exception e) {
//6.回滚事务
transactionManager.rollback(transaction);
throw e;
}
}
}
1、注入事务管理器PlatformTransactionManger
2、定义事务的配置DfaultTransactionDefinition(传播属性、隔离级别机制等,不设置就给的默认值)
3、开启事务
4、try catch:判断,成功就提交,失败就回滚
声明式事务:@Transactional
原理是AOP
1.在执行目标方法前创建事务管理器
2.设置事务传播属性以及事务隔离级别机制
3.开启事务
4.方法执行成功就提交事务,失败就回滚事务
@transactional(islation=Isolation.DEFAULT),这是设置事务隔离级别机制,默认用的-1,他其实就是应用层我们不设置隔离级别机制,以数据库的隔离级别为准
6、ACID
原子性(A):即不可分割性,事务要么全部被执行,要么就全部不被执行。
一致性(C):事务中数据状态在执行前后要保持一致(数据不能无故增加或减少)
事务必须使数据库从一个一致性状态变换到另一个一致性状态,即一个事务执行之前和执行之后都必须处于一致性状态。拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还是5000,这就是事务的一致性。
隔离性(I):事务执行期间不受其他事务所干扰。
持久性(D):事务一旦结束,数据就持久到数据库
7、SpringMVC执行流程以及对应的设计模式
对应两种设计模式:责任链模式以及适配器模式
- 第一步:发起请求到前端控制器(DispatcherServlet)
- 第二步:前端控制器请求处理器映射器HandlerMapping查找 Handler (可以根据xml配置、注解进行查找)
- 第三步:处理器映射器HandlerMapping向前端控制器返回Handler,HandlerMapping会把请求映射为一个执行链(HandlerExecutionChain)对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略
- 第四步:前端控制器调用处理器适配器去执行Handler
- 第五步:处理器适配器HandlerAdapter将会根据适配的结果去执行Handler
- 第六步:Handler执行完成给适配器返回ModelAndView
- 第七步:处理器适配器向前端控制器返回ModelAndView (ModelAndView是springmvc框架的一个底层对象,包括 Model和view)
- 第八步:前端控制器请求视图解析器去进行视图解析 (根据逻辑视图名解析成真正的视图(jsp)),通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可
- 第九步:视图解析器向前端控制器返回View
- 第十步:前端控制器进行视图渲染 (视图渲染将模型数据(在ModelAndView对象中)填充到request域)
- 第十一步:前端控制器向用户响应结果
8、JavaWeb三大组件
Listener、Filter、Servlet
9、SpringBoot优势
约定大于配置
开箱即用
内置tomcat
10、bean的生命周期
bean的定义--实例化(内存中申请一块区域,并给一些默认值,比如实物隔离级别机制,传播属性的默认值)--属性注入(注入富对象)--初始化(增强扩展,比如修改一下隔离级别机制和传播属性)--使用--销毁
11、SpringIOC注入的三种方式
构造方法注入
setter方法注入
属性注入(Autowired和Resources)
12、Autowired和Resources区别
注入方式不同:Autowired根据类型注入,而Resources先根据名称注入,如果找不到就根据类型注入
@Autowired
@Autowired()
@Qualifier("baseDao")
private BaseDao baseDao;
@Resources:
@Resource(name="baseDao")
private BaseDao baseDao;
来源不同:@Autowired属于Spring,@Resource属于Java
13、重定向和转发区别
重定向:两次请求,第一次请求到原始 URL,第二次请求到新的 URL
重定向特点:客户端发起两次请求,浏览器地址栏发生变化,用户有感知,可以访问不同服务器上的资源
转发:一次请求,服务器内部处理转发
转发特点:一次请求,浏览器地址栏不变,用户无感知,不能访问不同服务器的资源
14、如何实现session共享
一般来说,我们实现session共享有两种方式,一种是将session数据持久化到数据库,这样就算有多台服务器我们每次请求到达各个服务器的时候,会去数据库查找对应的session数据;但是这样的缺点也很明显,每次请求我们都要去数据库查询,会造成数据库压力大,并且一旦数据库挂了,我们的session数据就无法访问了
另一种是采用JWT来实现session共享,他的特点是我们第一次访问服务器的时候服务器会生成一个session,接下来服务器会把session传递给客户端,不保存在服务端,这样我们服务端就变成无状态的了;
15、JWT的组成
JSON WEB TOKEN的缩写,是一种session共享的解决方式
头部:里面存的是生成签名的算法以及JWT的格式,通过base64URL编码生成第一部分
负载:存储的不敏感的数据,比如JWT过期时间,用户昵称,用户id等,同样也是通过base64URL编码生成
签名:根据前两部分通过base64URL编码结合秘钥再根据加密算法生成,计算出的签名也会通过base64URL编码
这三个部分通过“.”拼接生成JWT
JWT特点
优点
对移动端友好
可以用于认证也可以存储数据
安全性高一些
JWT前两部分不加密,只有第三部分加密
数据存储在负载部分,并且不建议存储敏感数据
可以用于认证身份也可以用于存储用户信息
建议用Https协议传输
缺点:
一旦签发直到过期前都有效
一旦修改了用户的信息,这个JWT无法实时更改
16、为什么JWT前两部分是明文的
如果加密了,前端想要获取JWT里的数据必须先解密,那么我们就要把秘钥也交给前端用来解密我们加密的数据,这样如果有恶意攻击的人可以从前端轻松拿到我们的秘钥,这样就不安全了
17.springboot自动装载原理
@SpringBootApplication注解:它是一个复合注解,由@SpringBootConfiguration
, @EnableAutoConfiguration
, 和 @ComponentScan组成
@SpringBootConfiguration表明他是一个spring配置类
@ComponentScan扫描标记的包以及子包,用于组件扫描
@EnableAutoConfiguration启用了自动配置类
@EnableAutoConfiguration里面会导入一个AutoConfigurationImportSelector类,这个类里有一个
SpringFactoriesLoader这个类,通过SPI原理,它会遍历META-INF中的spring.factories
文件,文件里是key与value结构的,value是我们的全类名,从中读取并实例化指定的自动配置类
18、JDK的SPI和spring的SPI有什么区别
JDK的只支持接口
spring支持接口、类、注解
19、springmvc里两个适配器
SimpleControllerHandlerAdapter:适配实现了 Controller
接口的处理器,通过 handleRequest
方法处理请求
HttpRequestHandlerAdapter:适配实现了 HttpRequestHandler
接口的处理器,常用于处理原生 HttpServletRequest/Response
操作(如文件下载)
20、springboot优势(springboot与springmvc区别)
1、开箱即用:在MAVEN项目的pom文件中添加相关依赖包,然后使用对应注解来代替繁琐的XML配置文件以管理对象的生命周期
2、内置tomcat:
3、约定大于配置
21、ThreadLocal
-
ThreadLocal
提供了线程本地存储,保证每个线程都有自己独立的变量副本,适用于场景如会话管理(拦截器里面存放的用户信息)、数据库连接、事务管理(事务的传播属性与隔离级别机制等存放在threadlocal里,方便传递)、跨层数据传参、日志(MDC)、traceId全链路跟踪 -
它通过
ThreadLocalMap
为每个线程存储本地变量,并通过弱引用机制来避免内存泄漏。 -
使用
ThreadLocal
可以减少锁的使用和同步开销,但需要谨慎处理内存泄漏风险
ThreadLocal的作用:
主要作用:
-
线程隔离:ThreadLocal 提供了线程之间的数据隔离。当多个线程操作同一个对象时,可以通过 ThreadLocal 为每个线程分配一个独立的变量副本,从而避免多线程间的变量共享导致的数据不一致问题。
-
解决并发问题:在高并发场景下,ThreadLocal 可以用来存储线程相关的状态信息,这样可以减少线程间的竞争,提高程序的并发性能。
-
可以跨层,跨类跨方法传递变量
-
简化代码:使用 ThreadLocal 可以简化多线程环境下的编程模型,使得线程局部变量的访问变得像访问普通变量一样简单。
22、springboot配置绑定方式(springboot如何读取配置文件,注入属性值或Javabean)
SpringBoot 提供了以下 2 种方式进行配置绑定:
使用 @Value 注解(属性注入):绑定一个属性
@Value("${cn.smart.tokenx.key}")
private String tokenKey;
properties文件里:
cn.smart.tokenx.key=By123
-
使用 @ConfigurationProperties 注解:可以写一个类来绑定多个属性(配置Javabean)
@Data
@Configuration
@ConfigurationProperties(prefix = "cn.smart.tokenx")
public class TokenXProperties {
private String key;
private String pathPatterns="/api/**";//拦截的路径
private String excludePathPatterns="";//白名单
private int order=1;
private boolean enable;
}
properties文件里:
cn.smart.tokenx.enable=true
cn.smart.tokenx.path-patterns=/api/**/
cn.smart.tokenx.exclude-path-patterns=/api/user/login
cn.smart.tokenx.key=By123
cn.smart.tokenx.order=1
23、springboot配置文件的优先级
分为jar包内和jar包外
jar包外的优先级高于jar内
file:./config/application.properties 高(项目打包以后跟jar包同级的的config里的application.properties)
file:./application.properties
classpath:/config/application.properties
classpath:/application.properties 低
为什么要设置优先级:打成jar包以后修改配置不方便,要解压什么的,可以在jar包同级的文件里直接配置,需要重启项目
24、springboot常用注解
1、@SpringBootApplication(总代理)
包含@Configuration、@EnableAutoConfiguration、@ComponentScan通常用在启动类上。
2、@Repository
用于标注数据访问组件,即DAO组件。
3、@Service
用于标注业务层组件。
4、@RestController
用于标注控制层组件(如struts中的action),包含@Controller和@ResponseBody
5、@ResponseBody
它主要用于控制器(Controller)的方法上,用来指示该方法的返回值应该直接写入 HTTP 响应体中,而不是解析为视图名称。
6、@Component
泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
7、@ComponentScan
组件扫描。相当于<context:component-scan>,如果扫描到有@Component @Controller @Service等这些注解的类,则把这些类注册为bean。
8、@Configuration
指出该类是 Bean 配置的信息源,相当于XML中的<beans></beans>,一般加在主类上。
9、@Bean
相当于XML中的<bean></bean>,放在方法的上面,而不是类,意思是产生一个bean,并交给spring管理。
10、@EnableAutoConfiguration
让 Spring Boot 根据应用所声明的依赖来对 Spring 框架进行自动配置,一般加在主类上。
11、@AutoWired
byType方式。把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。当加上(required=false)时,就算找不到bean也不报错。
12、@Resource(name="name",type="type")
没有括号内内容的话,默认byName。与@Autowired干类似的事。
13、@RequestMapping
RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
14、@RequestParam
获取URL的参数, 用在方法的参数前面。
15、@PathVariable
路径变量。参数与大括号里的名字要相同。
16、@Profiles
Spring Profiles提供了一种隔离应用程序配置的方式,并让这些配置只能在特定的环境下生效。
任何@Component或@Configuration都能被@Profile标记,从而限制加载它的时机。
17、@ConfigurationProperties
Spring Boot将尝试校验外部的配置,默认使用JSR-303(如果在classpath路径中)。
你可以轻松的为你的@ConfigurationProperties类添加JSR-303 javax.validation约束注解:
25、spring日志框架
springboot内置的logback
还有log4j、log4j2,这些都是一个人开发出来的
26、cmd启动一个java项目:
java-jar a.jar --spring.profiles.active:dev
server.port:8080
27、MDC是什么
MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的Map,可以往其中添加键值对。这意味着每个线程都有自己的 MDC 上下文;
MDC底层其实就是Inheritablethreadlocal,它本质也是threadlocal,与普通threadlocal的区别就是Inheritablethreadlocal存储的信息可以共享给当前线程的子线程,而普通threadlocal不可以
28、ELK和EFK
E:Elasticsearch,是一个非关系型数据库,同时也是实时的分布式搜索和分析引擎,支持全文搜索、结构化搜索和分析,具有高可伸缩性和高可靠性
L:Logstash,收集、处理和传输日志数据,相当于日志收集器
F:Filebeat,轻量级的日志收集器,用于监控和传输日志文件,资源消耗低
K:Kibana,可视化的日志分析工具,拥有可视化界面
原理就是先通过Filebeat去采集我们指定目录下以.log结尾的日志,将日志信息存储到Elasticsearch中,最后通过Kibana去查看我们Elasticsearch中抓取的的日志信息
我们使用的是EFK
29、Inheritablethreadlocal和threadlocal区别
InheritableThreadLocal 继承了 ThreadLocal 类,还扩展了一些其他的功能
准确的说就是 InheritableThreadLocal 可以让子线程继承父线程的 ThreadLocal 的值,ThreadLocal 里的变量只能让当前线程使用
30、JDBC执行流程
通过class.forName注册驱动
获取数据库连接
执行SQL语句返回执行结果
处理执行结果
关闭连接
31、Mybatis四种拦截器
1.Executer执行器:拦截MyBatis执行器方法的执行
2.StatementHandler拦截器:拦截SQL语句的执行,可以在SQL语句执行之前修改或增强它们
3.ParameterHandler 拦截器:拦截SQL语句的参数设置,允许在将参数设置到SQL语句之前修改或验证它们
4.ResultSetHandler 拦截器:拦截从SQL语句返回的结果集的处理
32、Mybatis执行流程
mapperstatement在启动的时候就创建好了,有多少dao就有多少个mapperstatement
Sqlsession会话对象:用于管理与数据库的交互
Executor:执行器,负责执行 SQL 语句,管理缓存和事务。
ParameterHandler:参数处理器,负责将传入的参数绑定到 SQL 语句中。
ResultSetHandler:结果集处理器,负责将数据库返回的结果集转换为 Java 对象。
StatementHandler:语句处理器,负责准备 SQL 语句,包括生成最终的 SQL 语句和设置参数
MappedStatement对象:
- SQL 映射:
MappedStatement
对象包含了 SQL 语句的信息,如 SQL 语句的文本、参数映射、结果映射等。 - 参数映射:定义了如何将传入的参数映射到 SQL 语句中。
- 结果映射:定义了如何将查询结果映射到 Java 对象中。
33、Mybatis一二级缓存的区别
开启方式:一级缓存默认开启,无需配置,二级缓存需要在配置文件中手动开启
作用范围:一级缓存是sqlsession级别的,二级缓存作用于整个应用程序(SqlSessionFactory 生命周期内)
生命周期: 同一sqlsession执行增删改操作会清除sqlsession,二级缓存当SqlSessionFactory
销毁的时候也会随之清除
34、resultmap和resulttype的区别
resultType
:用于简单的结果映射,当 Java 对象的属性名称和数据库表的字段名称一致时,可以使用 resultType
resultMap:
用于复杂的映射场景,当 Java 对象的属性名称和数据库表的字段名称不一致时,或者需要处理嵌套对象、多表关联等情况时,可以使用 resultMap
。
35、反射哪里用到了
1.例如我们每个表中都有lastupdateby最近更新人这个字段,我们不想每次都是service业务层去给他赋值,所以我们将用户的信息放入token中,然后后端拿到token并解析获取用户的信息放入threadlocal中,我们使用了mybatis拦截器中的参数拦截器,从threadlocal中获取用户的昵称,并且通过反射来给lastupdateby这个字段赋值。
2.枚举类,枚举的时候统一返回枚举类的状态码和信息(code和desc)就用的反射
3.wapper。通过反射获取类上有没有NoWapper这个注解,有注解不做任何改变,没有注解时就进行包装
4.在使用Excel上传时,读文件里的数据将它转成对应的实体类,将Excel里每一行数据转换成对应实体类的属性,用到了反射。
36、如何实现session共享
场景:用户第一次访问服务器会在服务器内部生成一个session,但是不利于我们服务器扩容,比如我们第一个session存储在服务器1上,但是没有办法传送到服务器2,这个时候如果我们去访问服务器2就会拿不到我们刚才的session;
解决方法:
1、将session存储到持久层(比如数据库),这样我们每次访问都可以去数据库获取session,缺点是数据库压力会变大,如果数据库挂了所有session就会失效,
2、第二种是采用JWT实现session共享,用户登录后采用JWT生成一个token,token里存储我们用户的一些不敏感信息,比如用户名,角色,手机号登;生成完的token会返回给客户端并存储在客户端,每次请求的时候会携带这个token,这样服务器只需要校验这个token是不是我们下发的即可,服务器不需要存储用户的信息,服务器变为无状态了,方便扩容;
37、JDK1.8的新特性,stream流常用操作
1.过滤:使用filter()方法可以过滤掉集合中不符合条件的元素。
2.映射:使用map()方法可以对集合中的每一个元素进行映射处理。
3.排序:使用sorted()方法可以对集合中的元素进行排序。
4.去重:使用distinct()方法去掉集合中的重复的元素。
5.统计:使用count()方法可以对集合中的元素进行统计。
6.聚合:使用reduce()方法可以对集合中的元素进行聚合计算。
7.遍历:使用forEach()方法可以遍历集合中的每一个元素。
8.匹配:使用anyMatch()、allMatch()、noneMatch()方法可以对集合中的元素进行匹配判断。
9.分组:使用qroupingBy()方法可以按照某一个属性进行分组。
10.转换:使用collect()方法可以将集合中的元素转换为另一个集合。
11.平均:使用average()方法可以用于计算一组元素的平均值。
12.min:取最小值
13.max:取最大值
14.skip:跳过
38、exception和error的区别
exception异常是指程序执行期间发生的、可以被程序捕捉并处理的非正常事件
error错误指的是更加严重的问题,通常是程序无法恢复的情况,或者是程序设计中的缺陷
39、事务失效的的八种情况
1、自调用(Self-Invocation)
即自己调用自己,一个类的方法在调用同一个类的另一种方法的时候,事务会失效。
类内部非直接访问带注解标记的方法 B,而是通过类普通方法 A,然后由 A 调用 B。 自己玩自己
@Service
public class Demo {
public void A() {
this.B();
}
@Transactional
public void B() {
......
}
}
解决方法是将这些方法拆分到不同的类中,或者通过 Spring 的代理来调用这些方法。
在该Service类中使用AopContext.currentProxy()用AOP上下文获取代理对象,让代理对象去执行目标方法,前提是要在启动类里加上
@EnableAspectJAutoProxy(exposeProxy = true)//导出业务代码然后在目标方法上加上
@Transactional注解,在执行方法前开启事务,执行成功则提交事务,失败则回滚事务
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
@EnableTransactionManagement
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Java
((ServiceA)AopContext.currentProxy()).doSave(user);
或者
2.非public修饰的方法
@Transactional注解只能在在public
修饰的方法下使用。
/**
* 私有方法上的注解,不生效(因私有方法Spring扫描不到该方法,所以无法生成代理)
*/
@Transactional
private boolean test() {
//test code
}
3、数据库不支持事务
MySQL中,MyISAM引擎不支持事物,InnoDB 支持事物
4、异常类型不匹配
@Transactional 注解默认只管理运行时异常(如RuntimeException
及其子类)和错误(如Error
)。当抛出未被捕获的运行时异常(RuntimeException
)时,Spring 会触发事务回滚操作,将之前的操作撤销;对于受检异常(即必须被捕获或声明抛出的异常,例如IOException
,SQLException
等),除非你明确配置事务管理器去处理它们,否则事务不会回滚。
可以通过 rollbackFor 和 noRollbackFor 属性来指定需要回滚或不需要回滚的异常类型。
@Transactional(rollbackFor = SQLException.class)
@Transactional
public void insertAll(PoMaster master) throws Exception {
poMasterDao.insert(master);
if(1 == 1){
// 故意引发受检异常,事务默认不会回滚
throw new SQLException("A checked exception occurred.");
}
poItemDao.insertList(master.getItems());
}
5、捕获异常未抛出,自己消化掉了异常
@Transactional
public void A(){
try{
......
}catch(Exception e){
// 未抛异常
}
}
6、传播属性设置问题
前两个REQUIRED与SUPPORTS常用些
propagation属性错误
@Transactional默认的事务传播机制是:REQUIRED,若指定成了NOT_SUPPORTED、NEVER事务传播机制,则事物不生效
7、Bean没有纳入Spring IOC容器管理
该类没被Spring管理,事物也是不生效的
8、事务方法内启动新线程进行异步操作
主线程没有拿到子线程的异常,所以代理类以为正常执行了
40、你们权限怎么做的?(spring security的原理)
使用的是责任链模式,
首先,请求会先到我们的listener里再发送到filter,filter不仅只有一个,有多个,filter1到filtern(filter1到filtern是一个过滤链)之间有一个delegatingfilterproxy,他也是一个filter,这个filter里面有一个filterchainproxy,它里面有一个securityfilterchain过滤器链,里面有许多security filter,这个采用的是责任链模式,每个security filter负责各自的权限管理,默认有15种,在这个过滤器链之间我们可以手动添加我们自己的权限管理,比如添加JWTTokenFilter,解析token里的角色把它放到security上下文里边,看我们的方法上面有没有@ReAirchired这个注解,这个注解里看包不包含我们填在securitycontext上下文里的角色,如果包含继续执行方法(这里运用到AOP原理,在执行方法前做一些事情判断包不包含我们写入上下文里的角色),不包含则返回错误403(未授予权限);
41、RBAC相关的表
用户表
角色表
用户角色表
权限表
角色权限表
42、你在哪里使用了SPI技术
手写starter里,也就是springboot自动装载原理
比如 JDBC 驱动程序就是通过 SPI 机制来加载不同数据库厂商提供的驱动实现
43、你了解的设计模式
原型模式
适配器模式(springmvc里的handleradapter)
责任链模式(springsecurity)
观察者模式(出库)
策略模式(出库类型)
44、MongoDB应用场景
1、拼车小程序里的地图
2、电商商城的首页楼层
3、社交论坛,如博客,评论
4、游戏开发中的角色,比如角色的装备等级
45、TCP和UDP的区别
TCP(安全可靠的传输控制协议):
TCP是一个面向连接的协议,也就是客户端吧必须和服务端建立连接并且一方发送请求以后另外一方必须作出响应才能发送下一次请求;是一种面向连接的、可靠的、基于字节流的传输层通信协议.
UDP(用户数据报协议):
UDP是一个无连接协议,只管发送请求不管是否有响应;以独立的数据报形式传输数据,每个数据报都是独立的消息单元
TCP连接的三次握手和四次挥手:
三次握手:
四次挥手:
46、网络协议OSI七层结构
47、什么是全双工和半双工
全双工:客户端在给服务器端发送信息的同时,服务器端也可以给客户端发送信息;同时传递信息.
半双工:客户端可以给服务端发送信息,服务端也可以给客户端发送信息,但是客户端和服务端不能同时发;同一时刻,只能单向传递数据. 对讲机
48、日志链路跟踪如何实现(MDC)
先解释一下什么是MDC:
MDC 介绍 MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的Map,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据。
简而言之,MDC就是日志框架提供的一个InheritableThreadLocal,项目代码中可以将键值对放入其中,然后使用指定方式取出打印即可。
后续会补充。。。。。。。。。。。。。。
49、synchronized锁的升级过程
无锁--有锁--偏向锁--自旋锁--重量级锁
50、VM(虚拟机)和docker的区别
VM类似于一个完整的操作系统,是完全虚拟的环境,几乎所有的应用程序都可以在上面运行,所以启动时间可能比较长,占用更多的资源。而docker是容器化技术,必须与宿主机操作系统兼容,依赖于宿主机,并且它启动速度更快,占用资源少,两者并不是互相排斥的。
51、事务的隔离级别机制
读未提交:存在脏读问题
读已提交:存在不可重复读问题
可重复读:存在幻读问题
串行化:什么问题都没有,事务按顺序执行,但是效率低
52、有没有用过nginx
有,当时通过nginx发布前端项目,也是用nginx作为静态资源服务器,用来做万能上传,后续改为了使用阿里云OSS来存储这些静态资源;
nginx还可以作为
Web服务器:作为静态内容(如HTML文件、图片、CSS文件、JavaScript文件等)的Web服务器
反向代理服务器:将客户端请求转发给后端的其他服务器(例如应用服务器),然后将后端服务器的响应返回给客户端。这有助于负载均衡、提高性能和安全性
负载均衡器:通过分发来自客户端的流量到多个服务器上,来平衡网络应用的负载,从而提高应用程序的可用性和扩展性。
HTTP缓存:缓存从后端服务器获取的内容,并在后续请求中直接提供这些缓存的数据,以减少对后端服务器的压力并加快响应速度。
重定向:Nginx 的重定向功能主要用于将请求从一个 URL 重定向到另一个 URL,适用于多种场景。旧域名到新域名重定向、老url到新url的重定向、HTTP 到 HTTPS 重定向。
nginx里的负载均衡算法
轮询:这是默认的负载均衡方式,Nginx 会将请求按顺序轮流分发到每个后端服务器
随机:随机分发请求
权重:你可以为每个后端服务器设置不同的权重,这样就能控制请求的分发比例。例如,你可以给某台性能较强的服务器设置更高的权重,确保它处理更多的请求。
ip哈希:Nginx 会根据客户端的 IP 地址来决定请求转发到哪台服务器。这可以确保同一客户端的请求始终转发到相同的服务器,从而避免会话问题。
url哈希:根ip哈希差不多,指定我们发送的某个url固定打到哪个服务器
53、数据脱敏怎么实现
使用hutool里的脱敏工具:信息脱敏工具-DesensitizedUtil
例如对身份证号脱敏
// 5***************1X
DesensitizedUtil.idCardNum("51343620000320711X", 1, 2);
需要注意的是数据脱敏发生的时间点:脱敏发生的时间点就是我们真正要把这个对象写到网络中去,或者是写到磁盘里面去,也就是去进行序列化的时候,就会真正的进行脱敏的这个操作
可以通过自定义一个序列化器实现:JsonSerializer<T>
是 Jackson 提供的一个接口,用于自定义序列化逻辑。,用于定义如何将 Java 对象序列化为 JSON 格式的字符串。当 Jackson 需要将一个 Java 对象转换为 JSON 字符串时,它会调用实现了 JsonSerializer<T>
接口的类中的 serialize
方法。
54、CICD流水线步骤
选择代码源-->代码扫描以及单元测试-->Java构建以及镜像构建-->部署-->发送通知(钉钉通知或者邮箱通知)
流水线的好处
1、全自动,无需人工干预
2、定时定点
55、百万级数据导入
1、关键点在于执行器类型,有三种类型:
ExecutorType.SIMPLE:是mybatis默认的执行sql语句的方式,每次执行SQL语句时,如果 SQL 语句是简单的 SELECT
、INSERT
、UPDATE
或 DELETE
语句,mybatis会创建一个新PreparedStatement对象,并且每条SQL语句都会立即被执行并提交到数据库。因此其适用于常规的单个或少量SQL操作。
ExecutorType.BA TCH: 需要手动开启,ExecutorType.BATCH会将一批SQL语句集中在一起批量执行,减少了与数据库的交互次数,提高性能
ExecutorType.REUSE:当多次调用相同的SQL语句时,会重用已编译的SQL语句和执行计划。适用于单条SQL语句的重复执行,例如在一个循环中多次执行相同的SQL语句
2、另外一点就是使用hutool工具提供的通过sax解析器来读取excel,这个解析器的好处是可以逐行读入逐行处理并不是一次性读取所有excel数据到内存中
3、往数据库插入的时候使用线程池
PreparedStatement对象的作用:
①预编译并提高性能---在第一次执行时解析和优化 SQL 语句,后续执行时可以直接使用预编译的sql语句结果,从而提高性能。
②防止sql注入---会对sql语句结构进行 转码。
56、前端如何调用后端
1、使用 Axios,Axios 是一个流行的 HTTP 客户端库,它支持 promise,并且具有良好的错误处理机制以及自动转换 JSON 数据等功能
2、WebSocket,对于需要实时双向通信的应用场景,比如聊天室或在线游戏,可以考虑使用 WebSocket 协议。WebSocket 提供了一个持久连接,允许服务器主动推送消息给客户端。
57、哪里使用了AOP原理
1.手写的那些starter都用到了aop;
2.以及springsecurity权限管理那里,我们自定义了一个JWTSecurityFilter,这是一个关于JWT的权限过滤器,在使用的时候我们会先解析token,从里面获取用户的角色放入security上下文中,然后在我们那个@ReAirchired这个注解,把这个注解加在方法上,在执行方法前先判断这个注解里的角色是否被包含security上下文里的角色,如果包含了证明用户有权限;这里就用到了AOP
58、JDK自带线程池有哪些(JDK创建线程池的方式有哪些)
1、newCachedThreadPool():创建一个可缓存线程的线程池,如果线程池长度超过处理需要,可以回收空闲线程(开除短工),如果没有空闲线程可以回收就创建新的线程(工人不够就招收新的工人)
2、newScheduledThreadPool:创建一个定长线程池,可以周期性以及定时执行任务
3、newFixedThreadPool:创建一个定长线程池,可以控制最大线程数,超出的线程就在队列中等待
4、newSingleThreadExecutor():创建一个单线程的线程池,所有任务都在同一线程中按顺序执行
59、线程池的拒绝策略
-
AbortPolicy(默认):
- 当线程池和队列都满时,会抛出一个
RejectedExecutionException
异常,表示新提交的任务被拒绝。
- 当线程池和队列都满时,会抛出一个
-
CallerRunsPolicy:
- 如果线程池和队列已满,则由调用线程(即提交任务的线程)执行该任务。这种方式可以减慢任务的提交速度,从而给线程池一些喘息的时间来处理已经存在的任务。但是,这可能会阻塞提交任务的线程,直到任务完成。
-
DiscardPolicy:
- 当线程池和队列都满时,此策略会静默丢弃新提交的任务,不会对调用者产生任何通知或异常。
-
DiscardOldestPolicy:
- 此策略会尝试丢弃最旧的未处理任务请求(即队列中最前面的任务),然后尝试重新提交当前任务。如果再次失败,则重复这一过程,直到成功或者达到某种条件。
60、@PostConstruct
干啥的
这个注解用于标识一个方法,该方法应该在依赖注入完成后被容器自动调用,以便执行任何初始化工作。
作用:
- 初始化资源:通常用来执行一些必要的初始化操作,比如打开文件、建立数据库连接或启动其他服务。
- 单次调用:被
@PostConstruct
注解的方法只会被调用一次,即在bean的生命周期中仅在第一次创建bean之后调用(也就是bean的初始化的时候)。 - 无参数和返回值:此方法不能带有任何参数,也不应该有返回值。
61、幂等性体现在哪
幂等性是什么
幂等性是数学上的一个概念,在我们开发中可以理解成就是多次执行某个方法并且得到结果是一样的,那么我们就认为这个过程就是幂等性。
数据库幂等性:
查询天生支持幂等性:
#执行相同的查询语句只要在此期间没有修改数据,多次查询结果一致
select *from user where id = 10;
更新可能支持幂等性:
#支持幂等性的更新:每次更新的结果都一样
update user set name ='zhangsan'
#不支持幂等性的更新:每次更新后结果不一致
update user set age =age +1 where id= 20;
新增可能支持幂等性
#支持幂等性的新增
insert info user(user id, name,age)values(1,'zhangsan',20);
#不支持幂等性的新增
insert into user(name, age, nick name)values('zhangsan', 20,'sanzhang')
删除支持幂等性
#支持幂等性的删除
delete from user where id=21;
接口幂等性:
它的意思是对于相同的请求,无论请求多少次,返回的结果都一样
消息幂等性:
首先先看看不符合消息幂等性的场景
1、生产者重复生产:
生产者发送的消息MQ已经收到了,但是因为网络波动响应给服务器的时候超时,就会让服务器以为没有收到消息致使生产者再次投递消息;下游的消费者就需要消费两次个相同的消息,消费者需要自己做幂等性的处理。
2、MQ重试机制:消费者第一次消费id=1的消息时候,消费成功后相应给MQ的时候超时,让MQ以为没有消费,就会重新投递消息,导致消费两次
如何解决消息不幂等
生产端:状态检查,也就是说在发送消息前,先去查询数据库,看这个消息是否被处理过(根据单据状态),如果被处理过则忽略,否则继续处理并在处理后修改消息状态
消费端:使用唯一标识(BizId),在生产投递消息的时候就生成一个BizId,比如订单号,交易流水号等,并且要给BizId一些业务标识,以便我们清楚地知道是哪个服务的id
62、接口幂等性和接口防抖的区别
63、MySQL的binlog日志
bin.log日志记录了查询之外的操作(增删改)
这个日志有三种格式:
STATEMENT:记录所有的增删改操作的SQL(记录SQL)
ROW:记录每一行的数据变更(记录数据变更),但是较为占空间
MIXED:记录数据变更和操作数据库的SQL语句(记录SQL和数据变更)
64、DML和DDL
DML:数据操作语言,对数据的操作
DDL:数据定义语言,对数据结构的操作
65、为什么使用Redisson而不使用synchronized
1、synchronized(属于本进程的,JVM的锁)只能在本进程内部进行锁,不能实现分布式锁,是对本进程的锁,JVM层的
Redisson能实现分布式锁,
2、synchronized锁的TTL不好判定
3、setnx(TTL不好判定)可能造成死锁的问题,假如说我们不设置过期时间,但是在进程执行任务的时候出现异常或者进程挂了,那么锁就不会释放,一直持有,这样会造成死锁
而Redisson它有一个看门狗机制,假如说我们设置的过期时间为60s,有一个线程负责看我们的过期时间是否超过我们过期时间的三分之一,如果到了三分之一就自动续命,直到本线程执行完任务自动释放锁,并且如果在此期间应用挂了,因为我们设置的有过期时间为60s,时间一到自动释放锁;
66、isNotEmpty
和 isNotNull
区别
在Hutool工具库中,isNotEmpty
和 isNotNull
方法用于检查对象或集合是否为空或为null,但它们的用途和适用范围略有不同。
-
isNotNull(Object obj)
:这个方法是用来检查给定的对象是否不是null
。只要对象不是null
,它就会返回true
。只能检查对象是否为null -
isNotEmpty(Collection<?> coll)
或者isNotEmpty(String str)
等:这些方法则是专门用来检查特定类型的对象(如集合、数组、Map、字符串等)是否既不是null
也不是空的。他可以判断对象是否为null,空白字符串,空格。
67、MySQL定位慢SQL以及优化
1、xxl-job每天定时扫描我们的数据库,查看我们慢日志
2、拿到慢日志后去执行Explain命令查看SQL语句的执行计划
3、使用完explain命令后查看type列,看这个列的等级
const(使用了唯一索引且只返回一行数据)-->eq_ref(使用了等值连接)-->ref(用了非唯一索引,返回一行或多行数据)-->range(使用索引进行范围查询)-->index(全索引扫描,也就是用一个索引的全部)-->all(没用索引)
4、看看这个列有没有使用索引,如果没有使用看是否适合加索引,如何使用了索引看看是否要索引优化
5、看索引是否失效了,分析索引失效的情况并优化索引
68、百万级数据库的表要新增字段怎么办?
首先我们需要新开一张空表,例如我们的原表是order,我们新开一张order2,我们数据库的每张表中都有lastupdatetime这个字段,可以根据当前时间的最大时间为节点,小于这个时间的数据先拷贝到order2表中,拷贝完成以后将order改为order2,而order2表名改为order,接着再把原来order表中大于那个时间的数据增量同步到新的order表中即可;
69、JUC包的原子类
AtomicBoolean
: 提供了原子操作的布尔值。AtomicInteger
: 提供了原子操作的整数值。AtomicLong
: 提供了原子操作的长整数值。
70、synchronized和lock的区别
synchronized 和 Lock 都是Java 中用来解决线程安全问题的工具
1.特性区别:synchronized是Java内部的关键字而lock是JUC包下的接口,lock可以有许多实现类,比如ReentrantLock
2.用法区别:synchronized 可以写在需要同步的对象、方法或者特定代码块中;而lock需要显示指定起始位置和终止位置。且在加锁和解锁处需要通过lock()和unlock()方法显示指出。所以一般会在finally块中写unlock()以防死锁
3.synchronized可以自动上锁和释放锁,而lock需要手动上锁和释放锁
4.synchronized非公平锁,而lock可以配置上锁或释放锁
71、lock的实现类有哪些
ReentrantLock
- 这是最常用的
Lock
实现。它拥有与synchronized
相同的互斥锁语义,并且还提供了额外的功能,如可中断的锁获取尝试、超时的锁获取尝试以及公平性选择。 - 它可以配置公平或非公平锁,这意味着它可以按照请求锁的顺序来决定哪个线程获得锁。
ReentrantReadWriteLock
- 提供了三种模式的操作:读、写和乐观读。
- 它是一种非阻塞锁,可以提供更好的性能,特别是在高并发环境下。
72、synchronized锁在静态方法和非静态方法上的区别
非静态方法:锁的是当前实例对象,每个对象都有一个内置锁。如果一个线程进入了一个对象的同步实例方法,其他试图进入该对象任何同步实例方法的线程必须等待,直到第一个线程退出同步方法。
静态方法:锁的是该类的类对象,整个类的所有实例共享同一把锁
73、开启线程有几种方式?
1.继承Thread类
class MyThread extends Thread {
@Override
public void run() {
// 线程执行的代码
}
}
2.实现Runnable
接口,它允许你的类仍然可以继承其他父类
class MyRunnable implements Runnable {
@Override
public void run() {
// 线程执行的代码
}
}
3.实现Callable接口
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 42; // 返回计算结果
}
}
4.使用线程池
创建线程池的方式:
74、Runnable
和Callable接口的区别
1.返回值:Runnable
没有返回值,Callable结果有返回值
2.异常处理:Runnable
不能抛出受检异常(checked exceptions),而Callable可以抛出受检异常。
75、synchronized和分布式锁的区别
synchronized是Java语言级别的同步机制,它仅限于单个JVM(Java虚拟机)内部使用
分布式锁用来协调多个独立进程或跨多个JVM之间的资源访问
76、JUC的同步工具类有哪些
1.CountDownLatch:计数器闭锁,允许一个或多个线程等待其他线程完成一组操作后继续执行,里面有一个计数器,当计数器为0的时候继续执行下一个任务
2.CyclicBarrier:是另一种同步辅助类,它允许一组线程相互等待,直到它们都到达了一个共同的屏障点。CyclicBarrier
可以被重置和重复使用,因此称为“循环”的屏障
3.Semaphore:维护了一组许可证,每次获取许可时会减少一个可用的许可数目;当某个线程释放许可时,许可数目又会增加
77、分库分表怎么实现的
78、JDK1.8的新特性
- 引入了Lambda表达式(方法引用:通过
类名::方法名
形式对Lambda表达式进行简化) - 允许接口拥有方法体,default修饰的方法可以被子类继承和重写,static修饰的方法不能被子类继承
- 引入了函数式接口的概念,只有一个抽象方法的接口,通常用于支持lambda表达式和函数式编程风格
- Stream API是一种抽象流,可以通过函数式编程的方式处理集合中的数据
- Optional工具类,主要作用就是为了避免null值异常,防止NullpointException
- 引入了
java.time
包,包含全新的日期和时间API
79、抽象类和接口的区别
定义与实现:
- 抽象类: 可以包含抽象方法和具体方法,以及字段。
- 接口: 主要包含抽象方法;从Java 8起可以有默认方法和静态方法,所有成员变量都是
public static final
常量。
继承方式:
- 抽象类: 使用
extends
关键字,一个类只能继承一个抽象类。 - 接口: 使用
implements
关键字,一个类可以实现多个接口。
方法实现:
- 抽象类: 可以包含已实现的方法。
- 接口: 默认情况下只有抽象方法,但从Java 8开始支持默认方法和静态方法。
成员变量:
- 抽象类: 可以有实例变量、静态变量等。
- 接口: 只能有
public static final
的常量,不能有实例变量。
构造器:
- 抽象类: 可以有构造器用于初始化。
- 接口: 不能有构造器。
80、什么是线程安全问题
线程安全问题是指在多线程环境下,当多个线程同时访问和修改共享资源(如变量、数据结构或对象状态)时,可能会导致不一致的状态、错误的结果或程序行为异常的问题。
81、Windows查看对应端口哪个进程使用命令
netstat -ano | findstr 8080
杀死进程命令:taskkill /pid 进程id
82、XXL-JOB应用场景
1、ES商品中心第一次全量同步
2、MQ消息监控:监控消息队列中的哪些消息堆积或者没有被消费
3、订单完成,定期去扫描订单的状态以及下单日期,例如定期去扫描数据库中订单状态为配送中的订单,然后去调用快递100去查看订单的签收情况,如果签收了就把订单状态改为已完成,如果订单超过20天还是未签收状态,就需要将这些订单推送给人工客服那边让人工介入
4、MySQL每天早上使用xxljob推送慢SQL,例如我们每天早上8点将我们MySQL中的慢SQL推送到大数据部门或者发送到钉钉群
5、定期清理ES里十天以前的日志
83、GRPC协议
http1.0和http2.0区别
http1.0是采用线程池复用线程的思想
2.0采用的长连接思想,避免频繁握手挥手
84、你了解的RPC协议有哪些?
1、基于springcloud的openfeign,使用的http协议
2、GRPC(谷歌的):基于http2.0的长连接
3、阿里开源的Dubbo,基于dubbo协议,但是他的协议比较灵活可以自己在配置文件指定
85、MVCC机制(多版本并发控制)
它允许数据的读操作和写操作之间不相互阻塞,从而提高系统的并发性能
读操作不会阻止写操作,反之亦然。这是因为读取操作会访问数据的一个旧版本,而写操作则创建数据的新版本。查询查的是那个临时的数据快照,而不会因为其他正在进行的写操作被阻塞。
86、Java是解释性语言还是编译性语言
Java在运行的时候是解释性语言,在编译的时候是编译姓性语言
87、若依后台管理系统的优缺点
优点:
1、易于上手,他提供前后端分离的模版,前端使用vue,后端使用springboot
2、模块化设计,有多个模块,如权限管理,菜单管理,角色管理,内置的RBAC的几张表,不需要我们从0到1写了,提供了一些基础功能
3、有丰富的开发文档和示例,方便学习
4、集成了多种数据库,可以直接连接数据库,通过数据库中的表生成前后端代码
5、可以拖拽的形式开发表单页面
缺点:
1、需要一定的学习曲线
2、生成的前端页面粗糙一些,不太适合定制化开发,进行二次开发难度较大
88、有没有用过若依?
刚参加工作的时候有个客户确实要求使用若依来开发,若依确实提供了基础的RBAC相关的前后端模版,开发一些简单的页面比较方便,但是他不适合定制化开发,二次开发难度较高,我们公司都是自己从0到1写的后台管理系统,更符合我们的开发习惯,而且集成了许多组件,扩展性高一些
89、单例模式实现方式
1、饿汉式:饿汉式是最简单的实现方式,其实例在类加载的时候就创建好了。缺点是如果实例没有被使用,则会造成内存浪费
2、懒汉式:懒汉式是在第一次调用getInstance()
方法时才创建实例。缺点是延迟加载,避免了不必要的实例化。
3、双重检查锁:优化的懒汉式,线程安全,延迟加载且性能较好,缺点是
4、静态内部类:利用了ClassLoader的机制保证了线程安全性,并且实现了延迟加载
90、为什么使用starter不使用pagehelper
使用pagehelper任何地方使用的时候都需要写相关的分页参数,比如每页个数和分页大小,而且还要对结果进行处理,所以
91、bean的作用域
-
singleton(单例)
- 特点:默认作用域,整个Spring IoC容器中仅存在一个Bean实例,所有对该Bean的请求都返回同一对象。
-
prototype(原型)
- 特点:每次通过容器获取Bean时都会创建新实例,容器不跟踪其生命周期,依赖使用者自行管理资源释放。
-
request(请求)
- 特点:每个HTTP请求生成一个Bean实例,请求结束后实例销毁,仅适用于Web应用。
-
session(会话)
- 特点:同一HTTP会话(Session)共享一个Bean实例,会话结束则实例销毁,需Web环境支持
-
application(应用全局)
- 特点:整个Web应用程序生命周期内仅一个实例,作用域等同于ServletContext
92、arraylist和linklist区别
arraylist:底层是数组,默认大小为10,扩容倍数为1.5倍扩容,查询速度快,增删速度慢,时间复杂度为O(1)
linklist:底层是双向链表,没有扩容机制,适合增删,查询速度慢,时间复杂度为O(n)
93、union和unionall区别?
这两个都是组合查询,但是union会去重,unionall不会去重
94、linux发布项目三种方式
①打成jar包,发布到linux上,使用java -jar启动,也可以结合nohup以守护形式运行
②打成镜像,基于docker发布
③打成镜像,结合CICD流水线和K8S发布
94、数据库死锁了怎么办?
回滚事务粒度较小的事务
不建议重启数据库,代价太高
95、canal优缺点
优点:
1、与业务代码解耦
2、灵活性和扩展性
3、可靠性
4、能够增量同步并具有实时性
缺点:
1、对主数据库有侵入性,额外增加了一个同步节点
2、单点故障,要搭建集群
3、有些延迟,但是业务允许的延迟
96、left join左边尽量小表,right右边最好是小表,这样查询速度快
97、为什么使用mybatis而不使用mybatisplus
因为使用mybatis开发者可直接编写和优化 SQL,适合复杂业务场景,通过 XML 或注解明确管理 SQL 与 Java 对象的映射关系,便于调试和性能调优,简单来说就是灵活性更高
98、多线程环境下如何保证线程的安全性
1、加锁,使用synchronized或者实现lock接口,reentrantlock和reentrantreadwritelock
2、或者数据库层面使用事务
3、使用JUC包的原子类
99、核心线程数可以为0吗?
可以的,意思是就是长工为0,用的时候创建短工就行,但是最大线程数不能为0
100、封装、继承、多态分别指的什么?
封装:将对象的属性和行为(方法)封装到类中,隐藏内部实现细节,仅通过公开的接口与外界交互,特点是数据保护,简化复杂度
继承:子类自动获取父类的属性和方法,并可扩展或重写父类功能,实现代码复用和层次化设计
特点是提高代码复用性,多继承限制
多态:同一方法在不同场景下表现出不同行为,分为 编译时多态(重载) 和 运行时多态(重写)
101、重写equals方法为什么要重写hashcode?
equals方法是比较两个对象是否为同一个对象,根据 Java 规范,若两个对象通过 equals()
方法判断相等,则它们的 hashCode()
返回值必须相同
102、AQS(AbstractQueuedSynchronizer)是什么?
是Java并发编程中构建锁和同步组件的核心框架,通过 同步状态管理 和 线程排队机制 实现资源的高效协调。
核心组成:
同步状态(state):一个 volatile int
变量,用于标识共享资源状态
同步队列:一个FIFO双向链表队列,存储因竞争资源失败的线程节点
103、订单的状态
新建-已下单-支付完成-拣货中-配送中-订单完成-订单取消
104、采购单状态
新建(采购员)-审核通过(高级管理)-运输中-入库(采购员)-审核不通过(高级管理员可以)-作废(采购人跟高级管理者都可以)
105、spring、springmvc、springboot区别?
框架 | 定位 | 核心功能 | 典型场景 |
---|---|---|---|
Spring | 轻量级综合框架 | 1. IoC容器:通过依赖注入(DI)管理对象生命周期12 2. AOP:实现日志、事务等横切关注点13 3. 整合JDBC、ORM等数据访问技术2 | 企业级应用开发(非Web场景如后台服务) |
Spring MVC | Spring的Web模块 | 1. MVC架构:分离Model、View、Controller层3 2. DispatcherServlet:统一处理HTTP请求路由4 3. 支持RESTful、表单验证等Web特性5 | 传统Web应用开发(如电商前台、管理系统界面) |
Spring Boot | Spring的快速开发脚手架 | 1. 自动配置:根据依赖自动装配Bean12 2. 内嵌服务器:集成Tomcat/Jetty免部署4 3. Starter依赖:一键引入常用功能库(如JPA、Security)2 | 微服务、API接口快速搭建(如后台Restful服务) |
106、重载和重写区别?
重载:指的是在同一个类中允许存在多个同名方法,但是这些方法的参数列表必须不同(参数的数量、类型或顺序不同),返回类型可以相同也可以不同。
重写:方法重写是指子类重新定义父类中已有的方法,要求方法签名(包括方法名、参数列表)和返回类型完全相同,子类方法的访问权限必须 ≥ 父类方法(如父类为 protected
,子类需为 protected
或 public
)。
107、dubbo和springcloud区别
维度 | Dubbo | Spring Cloud |
---|---|---|
通信协议 | RPC(Dubbo 协议、Netty NIO),二进制传输,性能更高14 | HTTP REST,文本传输,灵活性优于性能14 |
接口约束 | 需严格定义 Java 接口,接口名/参数需完全一致,调试需关注序列化兼容性34 | REST 风格,接口定义宽松,支持 Postman 等工具直接调试3] |
调试复杂度 | 高(需处理接口版本、序列化异常等问题) | 低(HTTP 请求可直观查看请求体与响应) |
108、你如何看待信息安全问题?
1JWT:通过JWT生成的token,里面有JWT的加密算法
2接口防刷:
3验证码防刷:比如电商系统,我们的验证码接口要做防刷,针对一分钟内同一手机号只能请求一次,再对ip做防刷,同一ip一天只能调用50次
4数据脱敏:我们电商项目中地址管理中,我们对用户的地址进行了脱敏,以及用户的手机号脱敏
109、水平越权什么意思?
水平越权指的是同一权限等级的用户之间非法访问或操作彼此资源的安全漏洞,攻击者通过绕过权限校验,获取其他同级别用户的敏感数据或功能权限
场景:
①用户A请求查看订单时,接口仅验证Token有效性,未检查订单ID是否属于A
②某商城系统通过URL参数传递订单ID(如/order?id=1001
),攻击者修改ID值即可查看他人订单
解决办法是:
①权限分层设计:采用RBAC(基于角色的访问控制)模型,限制用户仅能访问自身角色关联的数据
②:数据校验:在查询、修改、删除操作时,需验证数据ID与当前用户身份(如Token绑定的用户ID)是否匹配
③加密敏感信息:对ID等参数加密(如JWT或UUID),避免直接暴露可遍历的数值
110、token存在哪?
前端在localstorage里,后端拿到直接解析放入threadlocal
111、单设备登录和单点登录
单设备登录(也属于单点登录的一个功能):同一账号 在同一时间只能在一个设备上登录,如果用户在另一台设备上登录,则之前的设备会被自动下线。实现:token往Redis里存一下,用hash里,大key:token.用户id,小key为token+设备type类型这个名称,每次登录去查询Redis的小key,登录以后把原来token删掉再加入新的token,原来的就不能用了
单点登录(SSO):用户在多个系统或应用之间只需要登录一次,就可以访问所有相关系统,无需再次输入凭据(无需重复验证即可访问多个关联系统)。例如QQ登录和王者荣耀登录,他们跳转的登录页面都是同一个,这就是单点登录,跳转到登录页面的时候会把从哪个页面跳转或者哪个平台跳转的信息带过来,登陆成功后重新跳回那个页面
112、创建对象的方式
①new关键字直接实例化
②通过反射机制:Class.forName()
+ newInstance()
③反序列化
④clone()方法,也就是克隆
⑤工厂模式
public class MyClassFactory {
public static MyClass createInstance() {
return new MyClass();
}
}
``` ```
113、深拷贝和浅拷贝区别
浅拷贝:对基本数据类型直接拷贝值,对引用类型(如对象、数组)仅复制其内存地址的指针,新旧对象共享同一块内存中的子对象
深拷贝:无论基本类型还是引用类型,均创建完全独立的新对象,新旧对象不共享任何内存
114、反序列化为什么要实现接口
接口的核心作用
-
标记机制
Serializable
是一个标记接口(无方法定义),其核心作用是向Java虚拟机(JVM)声明:该类的对象允许被序列化和反序列化。若未实现此接口,JVM会直接抛出NotSerializableException
异常13。 -
安全性验证
序列化涉及对象状态数据的持久化或传输,需确保类的设计符合序列化规范(如字段类型可控、无敏感数据泄露风险)。Serializable
接口的强制要求,可避免开发者无意中对不支持序列化的对象执行相关操作5。
- JVM的序列化流程
115、线程的方法stop和interrupt区别
stop(已废弃)是强制停止,直接终止线程,无论线程处于何种状态,interrupt向线程发送中断信号,由操作系统自行决定何时响应中断
116、分布式锁哪里用到了
下单的时候防重
我们下单做的三重防重
第一重:前端使用遮罩层,避免用户频繁点击按钮
第二重:后端使用Redis的setnx命令,拿到用户的id,String key = "order.lock2" + userId;把这个作为Redis的key,通过用户的ID生成一个唯一的锁标识符lock
,并尝试设置该键值对到Redis中。如果设置成功(即返回true
),表示获取到了锁,给这个锁设置过期时时间为10s;否则,表示操作频繁,需要稍后再试。
SETNX 命令只有当键不存在时才设置值
第三重采用redission看门狗,它的特点是当锁的存活时间达到三分之一时就自动续命;在获取了第二重锁之后,进一步通过Redisson提供的分布式锁RLock
进行更长时间的锁定,以确保业务逻辑能够完整执行而不会被重复处理;
为什么不直接使用redission呢?
因为redission看门狗这个锁相当耗费资源,我们使用setnx的过期时长不好把控,难免有些情况是我们线程没有执行完结果时间片没有了,所以这一部分防不住的交给第三重来防。
117、MySQL数据库索引结构
1、哈希索引
2、有序数组
3、树形索引:
B-Tree
- 结构特点:
- 优势:
- 减少树高度,降低磁盘 I/O 次数(适合机械硬盘随机读)3。
- 支持范围查询和前缀匹配。
B+Tree
- 优化点:
- 优势:
4、位图索引:为每个字段值建立位向量(bitmap),通过位运算快速筛选数据
118、你们下单成功后发送的mq都谁消费了?
1、会员中心,通过消费者去操作MySQL的积分记录并日志
2、商品中心区通过消费者修改ES销量
3、库存组通过消费者去减库存加销量
4、订单中心,延迟队列十分钟,十分钟后放入死信队列,死信队列消费者做判断,看订单状态,如果为未支付就去调用取消订单接口
119、IOC是什么?
IoC(Inversion of Control)是一种设计原则,其核心是通过外部容器管理对象的生命周期和依赖关系,而非由对象自身直接控制
特点:
控制权转移:对象创建、依赖绑定的控制权从代码内部转移到外部容器(如 Spring 容器)
解耦:对象之间无需硬编码依赖关系,通过容器实现松耦合,提高代码可维护性和扩展性
依赖注入(DI)是IOC实现的一种方式,指容器在运行时动态将依赖对象注入到目标组件中,而非由组件主动创建
120、线程池的状态
121、内存溢出和内存泄漏
内存泄漏:是指程序申请内存后未正确释放,导致该内存无法被再次使用,长期积累可能引发内存溢出,如静态集合持有对象引用
内存溢出:内存溢出指程序申请内存时系统无法提供足够空间,直接触发错误(如 Java 的 OutOfMemoryError
)。常见类型包括堆溢出(对象过多)、栈溢出(递归过深)等
122、你觉得成为一个中级(高级)开发需要具备什么?
1、会使用单元测试
2、遵守阿里开发规范
3、具备一定的线上解决问题以及代码调优能力
4、能够进行一些JVM的调优
5、对新知识新技术保持学习热情
6、良好的沟通能力以及团队协作能力,工作进度及时向上级汇报
123、代码优化过吗?
1、我们手写的starter(分页starter)
2、mybatis的拦截器(参数拦截器)
3、gateway的三大组件
4、枚举类(通过反射获取枚举类的code和message)
5、SQL优化
124、SQL注入是什么以及#为什么能防止SQL注入
SQL注入:SQL注入(SQL Injection)是一种针对数据库的安全漏洞攻击,攻击者通过在用户输入中嵌入恶意SQL代码,欺骗服务器执行非预期的数据库操作,其核心原理是:应用程序未对用户输入进行充分校验或过滤,直接将输入拼接到SQL语句中执行。
SQL注入案例:
这是正常的SQL
SELECT * FROM users WHERE username = 'admin' AND password = '123456'
攻击者在密码栏输入:' OR '1'='1
,此时生成的SQL语句变为
SELECT * FROM users WHERE username = 'admin' AND password = '' OR '1'='1'
该语句会返回所有用户数据,攻击者即可绕过密码验证直接登录
“#”防止SQL注入:
“#”是参数化查询的占位符标识之一,其本质是通过预编译机制将用户输入与 SQL 语句分离,从而避免恶意代码的注入
PreparedStatement 在代码执行前将 SQL 语句发送到数据库进行预编译,形成一个固定的执行计划(如 SELECT * FROM users WHERE username = ?
)。此时 SQL 的结构(如 WHERE 条件、表名、字段名)已确定,无法被用户输入修改
125、clone方法默认是浅拷贝
126、Java为什么有了字节流还要用字符流
为了解决字符编码问题,避免乱码
127、DCL双重检查锁的作用
第一重:后续满的线程就不需要与锁打交道了
第二重:第一次并发进来的时候,如果是多线程,避免进锁房间的线程重复创建实例对象
128、switch case 是分支结构的,也属于控制结构的
129、下单落库失败你们是怎么还积分的?
下单落库失败,我们使用TCC来实现归还积分和库存
130、你们的猜你喜欢怎么做的?
猜你喜欢这一块我们数据不是很精确,我们都是把他历史订单里根据商品分类给他去抓取一部分商品推送
131、阿里云OSS里存的什么
供应商的营业执照,视频、首页的轮播图、发现里的活动图片、活动视频、商品的图片
132、建索引的原则?
针对查询频率较高的字段,能建符合索引就建复合索引,尽量唯一索引,使用短索引,并且遵循最左匹配原则
133、你们微信登录和手机号登录的区别?
微信登录需要授权微信个人信息(如昵称、头像),存在隐私泄露隐患
而手机号登录只需要我们去数据库查询一下有没有这个手机号,再去发送验证即可
134、java线程报异常了怎么办
1、try-catch
捕获局部异常
2、Callable
+ FutureTask
捕获异常
3、全局异常管理:UncaughtExceptionHandler,需要实现
UncaughtExceptionHandler
接口,定义未捕获异常的全局处理策略(如日志记录、告警通知)
4、日志记录与监控整合
135、MVCC是什么?
多版本控制机制,为了解决幻读,他的原理是每次插入的时候会记录一个事务ID和undo_log指针,每次查询的条件是小于这个最大事务ID以及等于自身事务ID的数据,这样避免了幻读,undo_log指针是为了保证事务的回滚
136、MySQL的in和exit的区别
in扫描全表,exit判断是否存在,只要扫描到一个存在就不往下走了,不会扫描全表
137、xxl-job哪里用到了?
1、商品中心第一次全量同步使用xxl-job做个定时任务
2、MQ消息队列监控,使用xxl-job去扫描消息队列中是否存在堆积的消息
3、ELK清除日志十天前的
4、下单成功后定期扫描商品表状态为配送中的,再去调用xxl-job查看订单的状态,如果签收了就该订单状态为完成,没签收走人工把这些订单推送到客服那边
ES哪里用到了
商品中心全量同步,热销商品,商品搜索,ELK,
Redis哪里用到了
登录注册时发送的验证码存入Redis,
注册成功后送的积分存入Redis,
商城首页的楼层,商品分类,商品详情,
购物车(Redis的hash结构,大key:用户id,小key:商品id,value:商品操作的数量)
下单时候的防重:第一重前端遮罩层,第二重后端setnx,第三重redission
库存中心商品库存放入Redis,以及商品销量
MQ哪里用到了
下单成功推送消息到交换机
订单组:十分钟延迟队列,将消息放入死信交换机,再投递到死信队列,消费者去检查订单状态,如果是未支付就调用取消订单接口,如果支付了推送到仓储那边配货
商品中心:ES起一个消费者增加销量
会员中心:起消费者扣减MySQL积分,插入积分日志
库存中心:起消费者操作MySQL库存并插入库存日志
当生成出库单的时候,发一个MQ告知库存中心,减库存和履约量
138、异常处理机制
1、抛出(Throw)
2、try-Catch-Finally 结构:
try
块:包含可能产生异常的代码。catch
块:紧跟在try
块后,用来捕获并处理从try
块中抛出的异常。finally
块:无论是否发生异常,都会执行的代码块,通常用于清理资源。
139、mybatis接口绑定方式
1、XML文件绑定:将接口与 XML 文件关联,通过 namespace
指向接口全限定名,<select>
/<update>
等标签的 id
与接口方法名对应,实现方法到 SQL 的映射
2、注解绑定:在接口方法上直接添加 @Select
、@Update
等注解,并在注解内编写 SQL 语句,无需 XML 文件
140、mybatis的动态标签
1、<if>:用于单条件判断,如果 test 属性中的条件为 true,则包含其中的内容。
2、<choose>, <when>, <otherwise>:类似于 Java 中的 switch-case-default 结构,用于多条件分支选择。
3、<where>:自动处理 SQL 片段开头的 AND 或 OR,避免生成错误的 SQL 语句
4、<set>:通常用于更新操作中,自动添加 SET 关键字并去掉最后一个多余的逗号
5、<foreach>:用于循环集合数据,比如 IN 条件或者批量插入等场景。
6、<bind>:可以创建一个变量并绑定到 OGNL(Object-Graph Navigation Language)表达式的结果上,适用于一些复杂的查询条件拼接。
141、http报文体包含什么
请求头、请求体,url
141、线程池提交任务的方法
1、submit()
支持返回值:通过 Future
对象接收任务执行结果(包括成功返回值或异常信息)
2、execute()
无返回值:仅支持 Runnable
任务,任务执行完成后无法直接获取结果或异常
142、char和varchar区别?
char:固定长度,定义时需指定字符数(如 CHAR(10)
),实际存储不足时会自动填充空格至指定长度,插入时自动移除尾部空格,查询时返回结果不含填充空格
varchar:可变长度,保留尾部空格,查询时按原始数据返回,更节省空间
143、接口的默认方法和静态方法区别
关键字:默认方法是default修饰,静态方法是static修饰
调用方式:默认方法通过接口实现类的对象调用,静态方法通过接口名称直接调用
重写:默认方法可以被实现类重写,而静态方法不能被实现类重写
144、==与equals区别?
==:比较基本数据类型时比较值是否相等,比较引用类型时比较引用地址值是否相等
equals:继承自object类,仅用于比较引用类型,默认比较地址值,重写后可以自定义比较内容
145、&与&&区别
&:不具备短路与,这意味着即使第一个操作数能够决定整个表达式的结果,第二个操作数仍然会被计算(例如,在布尔运算中,如果第一个操作数为 false
,则无论第二个操作数是什么,整个表达式都将是 false
)。
&&:具备短路与,如果第一个操作数足以确定整个表达式的结果(如上述例子中的情况),那么第二个操作数将不会被评估,效率高
146、watch与computed以及watch和watcheffect
watch:watch监听的一个变量,只要这个变量改变了就会调用对应的回调函数
watcheffect:监听一组变量,只要这一组变量中的任何一个发生变化,都会触发回调函数
computed:是计算,他会监听一个或一组响应式变量,只要其中一个发生变化,所有的响应式变量都会重新计算
前两个类似于监听器,第三个就是重新计算
147、Java虚拟机的三个主要类加载器
启动类加载器(Bootstrap Class Loader):加载Java虚拟机的核心类库,例如object类,string类
扩展类加载器(Extension Class Loader):加载Java扩展类库的类加载器,JUC的工具包,Javax等包
应用程序类加载器(App Class Loader):负责加载应用程序的类,加载的是我们自己写的那些类
自定义类加载器
custom Class Loader
他们之间有父子关系,上层是下层的父亲:启动(爷爷)--扩展类(儿子)--应用程序(孙子)--自定义(曾孙)
148、类加载器的工作原理
1加载:根据类的全限定名(包括包路径和类名),定位并读取类文件的字节码
2链接:将类的字节码转换为可以在虚拟机中运行的格式
3链接可分为三个阶段:
验证:验证字节码的正确性和安全性,确保它符合Java虚拟机的规范
准备:为类的静态变量分配内存,并设置默认的初始值()
解析:将类的符号引用(比如方法和字段的引用)解析为直接引用(内存地址)。
4初始化:执行类的初始化代码,包括静态变量的赋值和静态块的执行
5使用
6卸载
149、双亲委派
简单解释一下就是每次类加载的时候,会先传入类的全限定类名,然后先从应用程序加载器开始判断这个类是否被加载过,但是应用程序会把他交给扩展类加载器判断是否加载过,扩展类会向上交给启动类判断是否加载过,等于说向上交给自己的父亲判断
为什么需要双亲委派?
1. 通过双亲委派机制,可以避免类的重复加载,当父加载器已经加载过某一个类时,子加载器就不会再重新加载这个类。
2. 通过双亲委派机制,可以保证类安全性。因为BootstrapClassLoader在加载的时候,只会加载JAVA_HOME中的jar核心类库,如java.lang.String,那么这个类是不会被加载。
150、servlet生命周期
1. 加载创建( instantiation): 当客户端首次请求Servlet时,Web容器(如Tomcat)会检查是否存在该Servlet的实例。如果不存在,则根据Servlet类的信息创建其实例。Servlet实例是由Servlet容器创建的,而不是由new关键字创建的。
2. 初始化(initialization): Servlet实例创建后,容器会立即调用Servlet的init()方法进行初始化。init()方法在整个生命周期内仅调用一次。在初始化阶段,Servlet可以加载一些持久性的资源,设置一些初始化参数等。
3. 响应服务(service): 客户端每次发送请求到Servlet时,容器都会创建一个新的请求和响应对象,然后调用Servlet的service()方法来处理请求。service()方法会根据HTTP请求的方法(GET、POST等)调用相应的doGet()或doPost()等方法。
4. 销毁卸载(destroy):
151、JKD动态代理和CGlab代理区别
152、进程和线程区别
进程是程序运行和资源分配的基本单位,一个程序至少有一个1进程,一个进程至少有一个线 程,但一个进程一般有多个线程。
进程在运行过程中,需要拥有独立的内存单元,否则如果申请不到,就会挂起。而多个线程能共享内存资源,这样就能降低运行的门槛,从而效率更高。
线程是是cpu调度和分派的基本单位,在实际开发过程中,一般单线程够用。
153、什么是线程死锁?死锁出现的条件?怎么避免死锁
死锁:
什么是线程死锁:两个或多个线程互相持有对方所持有的资源,会等待对方释放资源,若线程都不主动释放占有的资源就会产生死锁
死锁出现条件:
互斥条件(锁的互斥):一个资源一次只能被线程持有
保持条件:一个线程获取资源时,会一直持有这些资源,直到获取所有满足的资源才释放
不剥夺条件:已分配的资源不能被其他线程抢走
环路等待:多个线程形成一种循环等待的关系,互相持有对方所需资源,导致死锁
死锁产生这四个条件必须全部满足,少一个都产生不了死锁
怎么避免死锁:
1. 尽量避免使用多个锁,尽量使用一个锁。
2.减少锁的粒度, 确保同步代码块的执行时间尽可能短,这样可以减少线程等待时间,从而避免死锁的产生。
3. 使用尝试锁,通过 ReentrantLock.tryLock() 方法可以尝试获取锁,如果在规定时间内获取不到锁,则放弃锁。
4. 避免嵌套锁,如果需要使用多个锁,请确保它们的获取顺序是一致的,这样可以避免死锁。
154、string、stringbuffer、stringbuilder区别
string:线程安全,字符串常量,不可变,对于每一次的+=赋值都会创建一个新的对象
stringbuffer:线程安全(内部许多方法都是synchronized修饰的),字符串变量,可变,可在原来的基础上追加字符串,性能比string高
stringbuilder:线程不安全,字符串变量,可变,可在原来的基础上追加字符串,但是stringbuilder效率高于stringbuffer
155、hashset和linkedhashset区别
顺序:hashset无序, 不保证元素的插入顺序;linkedhashset有序, 维护元素的插入顺序
实现基础:hashset底层基于哈希表,linkedhashset底层也基于哈希表,但还维护一个双向链表