synchronized能够修饰代码块、静态方法、实例,确保数据的可见性和数据操作的原子性和程序执行顺序的有序性
锁的状态:(java6之后就对sychronized做了优化,不再是单纯的重量级锁)
无锁:
刚创建的对象为无锁状态
偏向锁:
当一个线程获得了锁,锁会进入偏向模式,当这个线程再次请求锁时无需同步,用于减少同一线程多次获得锁浪费时间,适合没有锁竞争的场景
轻量级锁(自旋):
当出现竞争时,锁的状态进入轻量级锁,适合线程交替执行同步块的场合,当存在同一时间访问同一锁就会导致失效,然后进入自旋状态,等待尝试获取
重量级锁(互斥锁):
当轻量级锁自旋多次还获取不到时或竞争激烈时,锁的状态膨胀为重量级锁
monitor对象监视器,是重量级锁的底层实现原理,就像一个房间,房间中含有被保护的数据,monitor保证每次只有一个线程进入该房间访问数据;monitor是由ObjectMonitor实现的(c++编写的),其中有很多参数如_owner(指向持有ObjectMonitor对象的线程地址)、_WaitSet(存放处于wait状态的线程)和_EntryList(存放处于等待锁阻塞状态的线程)
synchronized基于对象实现,java中的对象存储在堆内存中,包含对象头、示例数据和对象填充三部分,对象头中包含着MarkWord(包含四种锁信息、hashcode)和ClassPoint(.class文件),synchronized锁住代码块/方法,通过对象的monitor的取用(monitorenter)与释放(monitorexit)来实现同步/通过方法的class文件Access flags后边添加sychronized标识从而线程持有monitor-执行方法-释放monitor
Happens-Before是一种可见型模型,JMM(Java 内存模型)通过Happens-Before关系向开发人员提供跨域线程的内存可见性保证,如果一个操作的执行结果对另一个操作可见,那么两个操作之间必然存在Happens-Before关系;
Happens-Before关系只是描述结果的可见性,并不表示指令执行的先后顺序,也就是只要不对结果产生影响,仍然允许指令的重排序(指令重排序能提高执行效率)
Happens-before规则:
程序顺序规则:一个线程中的每个操作不管怎么重排序,线程执行结果不能改变
传递性规则:也就是 A Happens-Before B,B Happens-Before C推出 A Happens-Before C
volatile变量规则:对一个volatile修饰的变量的写happens-before于任意后续的读
监视器锁规则:一个线程对一个锁的释放锁操作,happens-before于后续线程的加锁操作
线程启动规则:线程A执行ThreadB.start()操作,之前的操作happens-before线程B中的任意操作
volatile的作用:
可以保证多线程环境下共享变量的可见性(某线程对共享变量修改,其他线程可以立即看到修改后的值,不可见主要是由CPU的三级缓存导致的一致性问题在并行下导致的)
可以屏蔽多线程环境下的CPU指令重排(多线程下CPU指令的编写顺序和执行顺序不一致,从而导致可见性问题,CPU提供的内存屏蔽指令就可以避免重排)
对于volatile修饰的共享变量,JVM会自动增加一个#Lock汇编指令(将当前处理器缓存行数据刷写到系统内存,这个刷写到主内存的操作会使其他cpu缓存的该共享变量内存地址的数据无效),根据CPU型号自动添加总线锁或缓存锁,并在变量写操作前后设置内存屏蔽禁止指令重排,还有变量读操作后插入两个内存屏障禁止后面的读指令和写指令重排。
volatile可以看成是轻量级的sychronized,volatile不能保证原子性,在操作本身为原子性的时候,使用volatile优于sychronized
总线锁:锁定CPU的前端总线,从而使同一时刻只能有一个线程去和内存通信
缓存锁:
总线锁导致CPU效率大大下降,缓存锁为了改进,只针对CPU三级缓存中的目标数据加锁
第一范式(1NF): 要求数据库中每个表必须具有原子性,即每个列的值都是不可再分的
第二范式(2NF): 满足第一范式的基础上,非主键列必须完全依赖于主键列
第三范式(3NF):
满足第二范式的基础上,要求数据库中的每个非主键列都不能传递依赖于主键,即必须直接依赖主键
BCNF(Boyce-Codd范式):
满足第三范式的基础上,任何非主键列都不能部分依赖于主键列。也就是说,主键确定的情况下,不能存在部分依赖于主键的情况
第四范式(4NF):
满足BCNF的基础上,消除多值依赖(多值依赖指的是在一个关系模式中,存在一组属性与另一组属性之间的多对多关系,即其中一组属性的值可能对应多个另一组属性的值)
Lambda表达式:以更简洁的方式来表示匿名函数或未命名方法,并将这些函数作为参数传递给方法或存储在变量中,使得代码更简洁
方法引用:对调用方法已经实现的函数的Lambda表达式进一步优化,省去了参数的传递
stream流:提供了一种简化集合和数组操作的新方式,提供很多方法(排序、比较等)
Optional类:可以用来防止空指针异常,不确定是否为空的对象可以用Optional包装
添加了新的日期API:localtime、clock、localdate(都不可变,适用于多线程环境)
接口更新:接口中可以用default或static关键字实现方法,
函数式接口:只包含一个抽象方法的声明(只有函数式接口才能缩写成lambda表达式)
加载启动类:加了@SpringBootApplication的启动类的main 方法中,通过运行SpringApplication.run()方法启动
<@SpringBootApplication是由@EnableAutoConfiguration(加载加了@Configuration的配置)、@SpringBootConfiguration(等同@Configuration)和@ComponentScan(自动扫描并加载符合条件的Bean)组成的 >
服务构建:构建SpringApplication
1.将资源加载器、主方法类记录在内存;
2.确定Web服务类型(Servlet-tomcat);
3.加载初始化类:读取注册初始化、上下文初始化、监听器的配置;
4.通过运行栈(stackTrace)判断main方法所在的类(启动类本身);
进入run方法
环境准备:
1.new BootstrapContext(上下文管理机制)
2.调用启动注册初始化器中的初始化方法(默认没有)
3.将java.awt.headless设置为true(表示缺少显示器键盘等输入设备也正常启动)
4.启动运行监听器(SpringApplicationRunListeners),然后发布启动事件(获取并加载spring-boot工程spring factories配置文件中的EventPublishRunListener,引入监听器配置)
5.构建可配置环境Enviroment(默认Servlet类型):加载系统环境变量、jvm系统属性、启动参数
6.将spring.beninfo.ignore=true(元数据不加载),打印Banner图
容器创建:
1.通过createApplicationContext来创建容器(上下文),创建Bean工厂,创建识别@Component注解、@Autowired注解等的几个自动注解处理器
2.通过prepareContext方法对容器属性初始化(加载Bean名称生成器、资源加载器、类型转换器),执行上下文初始化,为容器注册启动参数、引用策略、Banner、加载策略等
3.通过Bean定义加载器将启动类等资源定义集合加载到BeanDefinitionMap(Bean定义池),便于后续根据Bean定义创建Bean对象
填充容器:自动装配Bean
1.通过prepareRefresh方法,进一步配置环境(Enviroment),给servletContextInitParams和servletConfigInitParams赋值
2.调用obtainFreshBeanFactory方法(如果选择ClassPathXmlApplicationContext作为容器,会重构BeanFactory和重新加载Bean定义)
3.调用prepareBeanFactory方法加载类加载器、表达式解析器和配置文件处理器等系统级处理器和两个Bean后置处理器(解析Aware接口、处理自定义监听器注册和销毁),创建一些特殊Bean(BeanFactory、ApplicationContext、系统环境Enviroment、系统属性等)放入特殊对象池和单例池中
4.调用PostProcessBeanFactory方法对BeanFactory进行额外设置和修改(定义reques、session等servlet相关作用域scop,和ServletRequest、ServletResponse、HttpSession等特殊类)
5.执行InvokeBeanFactoryPostProcessors方法,执行BeanFactory后置处理器(其中包括配置处理器(加载所有@Configuration配置类)等)
服务熔断和服务降级是微服务架构中的常用容错机制,用于处理服务调用时的异常
服务熔断:
服务熔断是一种保护机制,用于当服务不稳定或故障时,防止对该服务的继续请求; 当服务调用失败时,断路器会打开,直接关闭服务,一段时间后断路器半开允许少量服务,如果成功率高,则关闭断路器恢复正常服务
服务降级:
当服务故障时或性能下降时,提供一个备用处理方案或者返回默认值,来保证用户或客户端能够获得基本的响应,而不是长时间的等待或错误;
ThreadLocal是另一种与加锁思路不同的解决多线程共享数据访问不一致问题的方法
ThreadLocal本身并不存储数据,它使用了ThreadLocals属性(在ThreadLocal中定义的ThreadLocalMap对象),当调用ThreadLocal的set(T value)方法时,将自身的引用(this)作为key,把用户传入的值作为value存储到Map中,这就相当于每个线程的读写操作都是基于线程自身的一个私有副本,线程之间的数据是相互隔离互不影响,这样基于ThreadLocal的操作就不存在线程安全问题。(空间换取时间的思路,提高程序的执行效率)
使用ThreaLocal要及时调用remove方法清理ThreadLocal变量,否则可能导致内存泄漏
mybatis缓存就是将用户经常查询的数据保存到内存中,提高查询效率,mybatis提供了一级缓存和二级缓存两种机制来优化数据库访问(分布式环境下不如直接用Redis等,开发成本低、更安全)
一级缓存(本地缓存):
用于保存用户在一次会话过程中查询的结果,用户一次会话中只能使用一个sqlSessiion,一级缓存自动开启不可关闭,默认先查Local Cache中,若没有再查数据库,并将数据放入Local Cache(Sqlsession持有的Excutor中的)
二级缓存(全局缓存):
是mapper级别的缓存,针对一个表的查询结果的存储,不同的sqlsession是可以共享的,也就是同一个namespace下的操作语句都连接同一个Cache
会话:会话就是一次完整的交流,包含多次请求响应,发送请求的为同一个用户
sqlsession:用户与数据库进行一次会话过程中使用的接口
垃圾回收机制主要是看对象是否有引用指向该对象,而程序员通过指定不同的引用可以决定某些对象的生命周期,有利于JVM进行垃圾回收
强引用:就是普通对象的引用,永远不会被垃圾回收
软引用:当内存空间不足时,才会被垃圾回收,通常用来实现内存敏感的缓存
弱引用:不管内存是否足够,都会被垃圾回收
虚引用:不影响对象生命周期,提供一种确保对象被finalize以后,内存回收之前采取行动