目录
2、服务注册和发现是什么意思?springcloud如何实现服务注册发现的?
方法四:使用CorsConfigurationSource实现跨域
2、mybatis的二级缓存的Serializable接口怎么实现?
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你知道吗
自旋的意思就是重新拉一份旧数据进行执行,保证线程的原子性。
线程池
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")
@CrossOrigin
public 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 {
@Override
public 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 {
@Override
public 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");
// 设置是否允许发送Cookie
response.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);
}
}
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void destroy() {
}
}
方法四:使用CorsConfigurationSource
实现跨域
通过实现CorsConfigurationSource
接口,可以动态地为不同的请求路径配置不同的跨域规则。
示例代码
@Configuration
public class CorsConfig {
@Bean
public 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); // 是否允许发送Cookie
configuration.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 Setters
public 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;
}
@Override
public 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
关键字,可以实现原子操作,从而保证线程安全。