使用线程池的好处
-
降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
-
提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
-
提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
ThreadPoolExcutor 核心参数
-
corePoolSize : 任务队列未达到队列容量时,最大可以同时运行的线程数量。
-
maximumPoolSize : 任务队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。
-
workQueue: 新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。
ThreadPoolExecutor其他常见参数 :
-
keepAliveTime:线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁。
-
unit : keepAliveTime 参数的时间单位。t
-
hreadFactory :executor 创建新线程的时候会用到。
-
handler :饱和策略。关于饱和策略下面单独介绍一下。
ThreadPoolExecutor 饱和策略
如果当前同时运行的线程数量达到最大线程数量并且队列也已经被放满了任务时,ThreadPoolTaskExecutor 定义一些策略:
-
ThreadPoolExecutor.AbortPolicy :抛出 RejectedExecutionException来拒绝新任务的处理(默认策略)。
-
ThreadPoolExecutor.CallerRunsPolicy :调用执行自己的线程运行任务,也就是直接在调用execute方法的线程中运行(run)被拒绝的任务,如果执行程序已关闭,则会丢弃该任务。因此这种策略会降低对于新任务提交速度,影响程序的整体性能。如果您的应用程序可以承受此延迟并且你要求任何一个任务请求都要被执行的话,你可以选择这个策略。
-
ThreadPoolExecutor.DiscardPolicy :不处理新任务,直接丢弃掉。
-
ThreadPoolExecutor.DiscardOldestPolicy : 此策略将丢弃最早的未处理的任务请求。
简单说一下 CallerRunsPolicy 策略,如果选择了这个策略,假如线程运行数达到 maximumPoolSize 并且 任务队列已满,此时再来新任务,线程池会将新任务推给任务调用者也就是主线程去执行,但是这样会导致主线程阻塞执行任务,可会导致主线程超时。
谈谈你对Bean生命周期的理解
Bean生命周期主要包含四个时期:实例化、属性赋值、初始化、销毁。
- 实例化:Bean 容器利用 Java Reflection API 创建一个 Bean 的实例。
- 属性赋值:Bean生命周期的属性赋值是指在Spring框架中,通过配置文件或注解的方式,为Bean对象的属性赋予特定的值或引用,以便在Bean对象创建、初始化、销毁等不同阶段,能够正确地处理这些属性。这些属性赋值可以通过构造函数注入、Setter方法注入、自动装配等方式实现。
- 初始化:如果 Bean 实现了InitializingBean接口,执行afterPropertiesSet()方法,如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
- 销毁:当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。
其他的诸如 BeanPostProcessor 接口、Aware方法只不过是对主流程四个步骤的一系列扩展点而已。
总结:
通过反射实例化 Bean 、设置 Bean 属性、检查 Aware 接口(拿到一些容器资源)、BeanPostProcessor 前置处理方法、初始化、BeanPostProcessor 后置处理方法、使用、销毁。
Bean的作用域
-
singleton、prototyp、request、session、application/globle-session、websockert
-
singleton : IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
-
prototype : 每次获取都会创建一个新的 bean 实例。也就是说,连续 getBean() 两次,得到的是不同的 Bean 实例。
-
request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
-
session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。
-
application/global-session (仅 Web 应用可用): 每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
-
websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。
说一说你对SpringAOP的理解
AOP:面向切面的编程,将横切逻辑代码和业务代码分离,可以提高代码复用率,降低模块之间的耦合。
如何控制切面执行顺序
在Spring AOP框架中,切面的执行顺序是根据切面的优先级来确定的。默认情况下,Spring AOP框架会按照切面定义的顺序依次执行。如果没有指定优先级,则默认情况下,切面的优先级是按照它们的声明顺序来确定的。
如果需要控制切面执行顺序,可以通过以下两种方式来实现:
-
使用@Order注解:@Order注解用于指定切面的执行顺序,它可以在切面类上使用。@Order注解的值越小,优先级越高。例如,@Order(1)的切面会先于@Order(2)的切面执行。如果没有使用@Order注解,则默认情况下,切面的优先级是按照它们的声明顺序来确定的。
-
实现Ordered接口:如果不想在切面类上使用@Order注解,可以让切面类实现Ordered接口,并实现其中的getOrder()方法。该方法返回一个整数值,表示切面的优先级。例如,返回1的切面会先于返回2的切面执行。如果没有实现Ordered接口,则默认情况下,切面的优先级是按照它们的声明顺序来确定的。
在实际应用中,可以根据需要使用@Order注解或实现Ordered接口来控制切面的执行顺序。
数据库隔离级别
- 读未提交:允许读取尚未提交的数据,可能会出现脏读、不可重复读、幻读;
- 读已提交:允许读取已经提交的数据,可以阻止脏读,,但是不可重复读或幻读仍有可能发生。
- 可重复读: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- 可串行化:最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
redis 五种数据类型和使用场景
Redis 共有 5 种基本数据结构:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。
String
用于存储字符串类型的数据,包括数字、文本、二进制数据等。
-
需要存储常规数据的场景(缓存 session、token、图片地址);
-
需要计数的场景(用户单位时间的请求数(简单限流可以用到)、页面单位时间的访问数、序列化后的对象(相比较于 Hash 存储更节省内存));
-
利用 SETNX key value 命令可以实现一个最简易的分布式锁(存在一些缺陷,通常不建议这样实现分布式锁)。
List(列表)
用于存储有序的字符串类型元素,支持从两端进行插入、删除等操作。
-
信息流展示 :最新文章、最新动态等;
-
消息队列:Redis List 数据结构可以用来做消息队列,只是功能过于简单且存在很多缺陷,不建议这样做。
Hash(哈希)
Redis 中的 Hash 是一个 String 类型的 field-value(键值对) 的映射表,特别适合用于存储对象,后续操作的时候,你可以直接修改这个对象中的某些字段的值。
- 对象数据存储场景:用户信息、商品信息、文章信息、购物车信息等。
Set(集合)
Redis 中的 Set 类型是一种无序集合,集合中的元素没有先后顺序但都唯一,有点类似于 Java 中的 HashSet 。当你需要存储一个列表数据,又不希望出现重复数据时,Set 是一个很好的选择,并且 Set 提供了判断某个元素是否在一个 Set 集合内的重要接口,这个也是 List 所不能提供的。
你可以基于 Set 轻易实现交集、并集、差集的操作,比如你可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。这样的话,Set 可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程。
-
需要存放的数据不能重复的场景:网站 UV 统计(数据量巨大的场景还是 HyperLogLog更适合一些)、文章点赞、动态点赞等场景;
-
需要获取多个数据源交集、并集和差集的场景:共同好友(交集)、共同粉丝(交集)、共同关注(交集)、好友推荐(差集)、音乐推荐(差集) 、订阅号推荐(差集+交集) 等场景。
-
需要随机获取数据源中的元素的场景:抽奖系统、随机。
Sorted Set(有序集合)
Sorted Set 类似于 Set,但和 Set 相比,Sorted Set 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列,还可以通过 score 的范围来获取元素的列表。有点像是 Java 中 HashMap 和 TreeSet 的结合体。
- 需要随机获取数据源中的元素根据某个权重进行排序的场景:各种排行榜。
Redis分布式锁删除不是原子性有什么影响
Redis分布式锁删除不是原子性,因为删除操作需要两步:
-
获取锁的持有者信息
-
删除锁
如果这两个操作不是原子性的,那么在获取持有者信息和删除锁之间,可能有其他线程也在尝试获取该锁,从而导致锁的重复获取或者丢失。因此,为了保证操作的原子性,可以使用Redis的Lua脚本或者Redlock算法来实现分布式锁的删除。
这里用我的理解解释一下:A 线程准备删除锁,首先 A 线程获取锁持有信息,并进行判断,判断通过后 A 线程正准备删除,此时,A 线程的锁失效了,而 B 线程刚好获取锁成功,A 线程继续删除,此时删除的已经是 B 线程的锁,因为 A 线程已经完成了获取所持有信息,并通过了判断,所以即使此时所持有者信息应为 B 线程,但是 A 线程也可以成功删除。