目录
mybatis:
1、mybatis的执行流程
2、mybatis是否支持延迟加载?
当我们需要去开启全局的懒加载时:
3、mybatis的一级和二级缓存
微服务
1、springcloud五大组件有哪些
2、服务注册和发现是什么意思?springcloud如何实现服务注册发现的?
3、负载均衡时如何实现的?
ribbon的负载均衡策略有哪些?
编辑
4、什么是服务雪崩,怎么解决这个问题?
5、你们的微服务是怎么监控的?
6、微服务限流
7、分布式系统理论
8、分布式事务的解决方案
线程安全
1、synchronized关键字的底层原理
2、synchronized底层原理进阶
3、你谈谈JMM(java的内存模型)
4、CAS你知道吗
5、synrinized和rentrentlock的区别以及实现原理
线程池
1、线程池的核心参数和执行原理?编辑编辑
2、线程池中有哪些常见的阻塞队列?
3、如何确定核心线程数?
4、线程池的种类有哪些?(executors不建议用)
5、为什么不建议使用executors创建线程池?
6、线程池的使用场景?
1、countDownLatch
编辑
2、数据汇总
3、异步调用
7、如何控制某个方法允许并发访问线程的数量?
一些其他问题:
1.Springboot如何解决跨域问题?
方法一:使用@CrossOrigin注解
1. 作用于方法
2. 作用于类
方法二:配置WebMvcConfigurer实现跨域
说明
方法三:使用CorsFilter实现跨域
示例代码
方法四:使用CorsConfigurationSource实现跨域
示例代码
方法选择建议
2、mybatis的二级缓存的Serializable接口怎么实现?
实体类实现 Serializable 接口
3、线程的原子性指的是什么
原子性的关键点
常见的原子操作
如何保证原子性
原子性的重要性
总结
mybatis:
1、mybatis的执行流程
1、读取mybatis-config.xml文件确定连接的是什么数据库,扫描的是哪些Mapper文件。
2、创建一个sqlsessionFactory会话工厂,它可以创建多个sqlsession会话,包含了执行SQL语句的方法
3、执行器是真正操作数据库接口的,并且维护一些缓存
4、MapperedStatement对象会封装某些信息,比如Mapper映射文件的名称、映射文件的哪个方法、执行的SQL语句、数据库返回的结果
5、将java传来的参数类型转化为数据库支持的数据类型。执行完后要转为java类型。
2、mybatis是否支持延迟加载?
立即查询:
延迟加载:
当我们不需要获取订单信息,系统不会执行查询订单信息的SQL语句。
当我们需要去开启全局的懒加载时:
<setting name="lazyLoadingEnabled" value ="true"/>
原理:创建一个代理对象
3、mybatis的一级和二级缓存
作用于同一个session的意思就是,通过同一个session执行的方法。
添加<cache/>标签。
两个查询语句只会执行一个SQL语句(同一个Mapper文件的方法):
3的意思是第一个sqlsession结束才能将一级缓存转移到二级缓存。
微服务
1、springcloud五大组件有哪些
配置中心写在springcloud.config文件中。
zuul被淘汰了。
2、服务注册和发现是什么意思?springcloud如何实现服务注册发现的?
ap是高可用模式,cp是强一致模式。
3、负载均衡时如何实现的?
ribbon的负载均衡策略有哪些?
4、什么是服务雪崩,怎么解决这个问题?
一个服务的所有连接数是确定的,当服务A向服务D发送请求失败时,连接并不会被释放,A持续向D发送请求会占满A的最大连接数,故而A无法再提供服务,A发生了宕机,而这是一个链式的崩溃过程。
解决方法:
服务降级
服务熔断
服务降级是针对接口,当服务的某个方法不可用时,我们对该接口的方法进行降级,它会直接返回错误而不占用连接,而仅仅是部分方法不可用不需要关闭整个服务,当请求失败率达到50%以上则触发熔断,整个服务会被关闭,进入熔断机制。熔断针对的是整个服务不可用。
5、你们的微服务是怎么监控的?
解决如下四个问题。
6、微服务限流
漏桶算法指的就是以固定速率漏出请求,超出桶容限的请求等待或抛弃。漏桶的请求数量更加平滑。
漏桶它的请求数是可以波动的,因为令牌数量可以累计,如果某一时间请求数很大会拿走大量的令牌。默认使用Redis存储令牌桶,所以需要在网关的配置文件中配置Redis连接。
7、分布式系统理论
要么是cp要么是ap
8、分布式事务的解决方案
线程安全
1、synchronized关键字的底层原理
汇编执行流程:
2、synchronized底层原理进阶
对象的对象头中存有Markword,Markword会存有monitor的地址,从而关联对象锁和monitor。
重量级锁:
轻量级锁的执行流程:
1、创建一个线程以后,线程里面会生成一个lock Record,里面的Object reference会指向对象锁,lock recode地址00存储锁地址然后与对象锁的marokword进行内容交换,那么对象锁的markword就会从无锁的状态转为轻量级锁(如果多个线程竞争一个对象锁,会升级为重量级锁),而tread-0中就会存储对象锁的原本内容,比如hashcode这些。
2、如果线程1执行中调用了线程2,则称之为锁重录,锁重录不是两个线程竞争对象锁,它仍然是一个线程,所以用轻量级锁即可。
3、发生锁重录后,线程Thread-0会再创建一个lock record,里面的lockrecord地址00会再与对象锁发生CAS连接,但是不发生交换,里面的内容为Null。
4、当线程逐渐执行完时,为lock record地址00为null的先执行完并被删除,CAS不会发生交换。而最后不为null的Lock Record会先与对象锁发生CAS交换,Markword部分再次变为无锁状态。然后LockRecord被删除,线程执行完。
偏向锁:
3、你谈谈JMM(java的内存模型)
4、CAS你知道吗
自旋的意思就是重新拉一份旧数据进行执行,保证线程的原子性。
5、synrinized和rentrentlock的区别以及实现原理
性能考量:
Java 6后synchronized经过优化(偏向锁、适应性自旋)性能大幅提升
低竞争场景:synchronized更优(JVM内置优化)
高竞争场景:ReentrantLock更优(可配置策略)
选型建议:
简单同步:优先使用synchronized(简洁、自动管理)
需要高级功能时使用ReentrantLock:
1、可中断锁获取(finally { lock.unlock(); })
2、超时控制(if (lock.tryLock(1, TimeUnit.SECONDS)) {)
3、公平性需求(// 非公平锁实现,允许插队
static final class NonfairSync extends Sync { ... }
// 公平锁实现,严格FIFO
static final class FairSync extends Sync { ... })
4、多个条件变量(// ReentrantLock支持多个条件
5、Condition notEmpty = lock.newCondition();
6、Condition notFull = lock.newCondition();)
需要知道锁是否被持有
必须在finally中手动释放
线程池
1、线程池的核心参数和执行原理?

具体实现:
2、线程池中有哪些常见的阻塞队列?
3、如何确定核心线程数?
开发时一般都是io密集型任务。
4、线程池的种类有哪些?(executors不建议用)
具体实现:会提供一个延迟执行的效果。
5、为什么不建议使用executors创建线程池?
6、线程池的使用场景?
1、countDownLatch
具体实现:
真实案例:
1、设置好countDownLatch(总页数),每一个线程完成一个页面数据的导入,只有循环执行完总页数大小的线程数量才能导入完所有的数据。
2、每一页数据导入到es的任务是提交给线程池执行。
2、数据汇总
具体实现:
串行执行:
并行执行:
3、异步调用
搜索记录的保存。
@ASync注解
7、如何控制某个方法允许并发访问线程的数量?
控制一个方法内最多使用的线程数量。
一些其他问题:
1.Springboot如何解决跨域问题?
方法一:使用@CrossOrigin
注解
@CrossOrigin
注解是Spring框架提供的一个非常便捷的方式来解决跨域问题,它可以直接作用于Controller类或方法上。
1. 作用于方法
在具体的Controller方法上添加@CrossOrigin
注解,仅对该方法生效。
@RestController
@RequestMapping("/api")
public class MyController {@GetMapping("/test")@CrossOriginpublic String test() {return "Hello, CrossOrigin!";}
}
这样,只有/api/test
接口允许跨域请求。
2. 作用于类
将@CrossOrigin
注解添加到Controller类上,该类中的所有方法都将允许跨域。
@RestController
@RequestMapping("/api")
@CrossOrigin
public class MyController {@GetMapping("/test1")public String test1() {return "Hello, CrossOrigin!";}@GetMapping("/test2")public String test2() {return "Hello, CrossOrigin!";}
}
这种方式适合整个Controller类中的所有接口都需要跨域的情况。
方法二:配置WebMvcConfigurer
实现跨域
通过实现WebMvcConfigurer
接口并重写addCorsMappings
方法,可以全局配置跨域规则。
@Configuration
public class GlobalCorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {// 配置跨域规则registry.addMapping("/**") // 允许跨域的路径.allowedOrigins("*") // 允许跨域的来源,*表示所有.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的请求方法.allowedHeaders("*") // 允许的请求头.allowCredentials(true) // 是否允许发送Cookie.maxAge(3600); // 预检请求的有效期}
}
说明
-
allowedOrigins
:允许哪些来源进行跨域请求,*
表示允许所有来源。 -
allowedMethods
:允许哪些HTTP方法进行跨域请求,如GET
、POST
等。 -
allowedHeaders
:允许哪些请求头。 -
allowCredentials
:是否允许发送Cookie和HTTP认证信息。如果设置为true
,则allowedOrigins
不能为*
,必须指定具体来源。 -
maxAge
:预检请求的有效期,单位为秒。
方法三:使用CorsFilter
实现跨域
通过自定义CorsFilter
来实现跨域,这种方式更加灵活,可以对请求进行更细粒度的控制。
示例代码
@Component
public class MyCorsFilter implements Filter {@Overridepublic void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {HttpServletResponse response = (HttpServletResponse) res;HttpServletRequest request = (HttpServletRequest) req;// 设置允许的来源response.setHeader("Access-Control-Allow-Origin", "*");// 设置允许的请求头response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me");// 设置允许的请求方法response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");// 设置是否允许发送Cookieresponse.setHeader("Access-Control-Allow-Credentials", "true");// 设置预检请求的有效期response.setHeader("Access-Control-Max-Age", "3600");// 如果是OPTIONS请求,直接返回if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {response.setStatus(HttpServletResponse.SC_OK);} else {chain.doFilter(req, res);}}@Overridepublic void init(FilterConfig filterConfig) {}@Overridepublic void destroy() {}
}
方法四:使用CorsConfigurationSource
实现跨域
通过实现CorsConfigurationSource
接口,可以动态地为不同的请求路径配置不同的跨域规则。
示例代码
@Configuration
public class CorsConfig {@Beanpublic CorsConfigurationSource corsConfigurationSource() {CorsConfiguration configuration = new CorsConfiguration();configuration.setAllowedOrigins(Arrays.asList("*")); // 允许的来源configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); // 允许的请求方法configuration.setAllowedHeaders(Arrays.asList("*")); // 允许的请求头configuration.setAllowCredentials(true); // 是否允许发送Cookieconfiguration.setMaxAge(3600L); // 预检请求的有效期UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", configuration); // 配置路径return source;}
}
方法选择建议
-
如果只需要为个别接口或Controller类解决跨域问题,可以使用
@CrossOrigin
注解。 -
如果需要全局配置跨域规则,推荐使用
WebMvcConfigurer
或CorsConfigurationSource
。 -
如果需要更细粒度的控制,例如动态调整跨域规则,可以使用
CorsFilter
或CorsConfigurationSource
。
根据实际需求选择合适的方法即可。
2、mybatis的二级缓存的Serializable接口怎么实现?
实体类实现 Serializable
接口
首先,定义一个实体类并实现 Serializable
接口。例如,定义一个 User
类:
import java.io.Serializable;public class User implements Serializable {private static final long serialVersionUID = 1L;//看来最主要的就是这句话private Long id;private String name;private String email;// Getters and Setterspublic Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +", email='" + email + '\'' +'}';}
}
二级缓存配置参数说明
eviction:指定缓存回收策略,可选值有:
FIFO(先进先出)
LRU(最近最少使用)
SOFT(软引用)
WEAK(弱引用)
flushInterval:缓存刷新间隔时间(毫秒),默认不刷新。
size:缓存的最大对象数。
readOnly:是否只读。如果设置为 true,则缓存的对象是只读的,不能被修改;如果设置为 false,则缓存的对象可以被修改。
3、线程的原子性指的是什么
线程的原子性(Atomicity)是指在多线程环境中,一个操作或一系列操作要么全部完成,要么全部不完成,不会出现部分完成的情况。换句话说,原子操作在执行过程中不会被其他线程中断。
原子性的关键点
-
不可分割性:
-
原子操作是不可分割的,不会在操作的中间被其他线程打断。
-
例如,一个线程正在执行某个原子操作时,其他线程无法插入执行,必须等待该操作完成。
-
-
线程安全性:
-
原子操作通常用于解决多线程环境下的线程安全问题。
-
如果多个线程同时访问和修改共享资源,原子操作可以确保操作的完整性,避免数据不一致。
-
常见的原子操作
-
单个变量的读写操作:
-
对于基本数据类型(如
int
、long
、double
等),单个变量的读写操作通常是原子的。 -
例如,
x = 10
是一个原子操作。
-
-
复合操作:
-
一些复合操作(如
x++
、x += 10
)在多线程环境下可能不是原子的。 -
例如,
x++
实际上包括三个步骤:读取x
的值、加 1、写回x
的值。如果多个线程同时执行x++
,可能会导致数据竞争(Race Condition)。
-
如何保证原子性
-
使用同步机制:
-
javasynchronized
关键字:在 Java 中,synchronized
可以确保多个线程不会同时进入同步代码块。复制
public class Counter {private int count = 0;public synchronized void increment() {count++;}public synchronized int getCount() {return count;} }
-
javaReentrantLock
:ReentrantLock
提供了比synchronized
更灵活的锁机制。复制
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class Counter {private final Lock lock = new ReentrantLock();private int count = 0;public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public int getCount() {lock.lock();try {return count;} finally {lock.unlock();}} }
-
-
使用原子类:
-
Java 提供了
javajava.util.concurrent.atomic
包,其中包含一系列原子类(如AtomicInteger
、AtomicLong
、AtomicReference
等)。复制
import java.util.concurrent.atomic.AtomicInteger;public class Counter {private final AtomicInteger count = new AtomicInteger(0);public void increment() {count.incrementAndGet();}public int getCount() {return count.get();} }
-
-
使用
volatile
关键字:-
volatile
修饰的变量可以保证可见性,但不能保证复合操作的原子性。 -
例如,
javavolatile
可以确保多个线程看到变量的最新值,但x++
仍然不是原子操作。复制
public class Counter {private volatile int count = 0;public void increment() {count++; // 不是原子操作}public int getCount() {return count;} }
-
原子性的重要性
-
避免数据竞争:在多线程环境下,原子性可以避免多个线程同时修改共享资源导致的数据不一致问题。
-
保证线程安全:通过原子操作,可以确保程序的正确性和稳定性。
总结
原子性是多线程编程中的一个重要概念,它确保操作的不可分割性和完整性。通过使用同步机制(如 synchronized
、ReentrantLock
)、原子类(如 AtomicInteger
)或 volatile
关键字,可以实现原子操作,从而保证线程安全。