点赞再看,养成习惯,微信搜索【三太子敖丙】关注这个互联网苟且偷生的工具人。前端
本文 GitHub github.com/JavaFamily 已收录,有一线大厂面试完整考点、资料以及个人系列文章。java
前段时间敖丙不是在复习嘛,不少小伙伴也想要个人复习路线,以及我本身笔记里面的一些知识点,好了,丙丙花了一个月的时间,整整一个月啊,给你们整理出来了。mysql
一上来我就放个大招好吧,个人复习脑图,能够说是全得不行,为了防止被盗图,我加了水印哈。nginx
这期看下去你会发现很硬核,并且我会持续更新,啥也不说了,看在我熬夜一个月满脸痘痘的份上,你能够点赞了哈哈。git
注:若是图被压缩了,能够去公众号【三太子敖丙】回复【复习】获取原图github
Spring Core:框架的最基础部分,提供 IoC 容器,对 bean 进行管理。web
Spring Context:继承BeanFactory,提供上下文信息,扩展出JNDI、EJB、电子邮件、国际化等功能。面试
Spring DAO:提供了JDBC的抽象层,还提供了声明性事务管理方法。redis
Spring ORM:提供了JPA、JDO、Hibernate、MyBatis 等ORM映射层.算法
Spring AOP:集成了全部AOP功能
Spring Web:提供了基础的 Web 开发的上下文信息,现有的Web框架,如JSF、Tapestry、Structs等,提供了集成
Spring Web MVC:提供了 Web 应用的 Model-View-Controller 全功能实现。
singleton(单例) prototype(原型) request session global session
resource定位 即寻找用户定义的bean资源,由 ResourceLoader经过统一的接口Resource接口来完成 beanDefinition载入 BeanDefinitionReader读取、解析Resource定位的资源 成BeanDefinition 载入到ioc中(经过HashMap进行维护BD) BeanDefinition注册 即向IOC容器注册这些BeanDefinition, 经过BeanDefinitionRegistery实现
定义BeanDefinitionReader解析xml的document BeanDefinitionDocumentReader解析document成beanDefinition
过程在Ioc初始化后,依赖注入的过程是用户第一次向IoC容器索要Bean时触发
若是设置lazy-init=true,会在第一次getBean的时候才初始化bean, lazy-init=false,会容器启动的时候直接初始化(singleton bean);
调用BeanFactory.getBean()生成bean的;
生成bean过程运用装饰器模式产生的bean都是beanWrapper(bean的加强);
其实就是经过在beanDefinition载入时,若是bean有依赖关系,经过占位符来代替,在调用getbean时候,若是遇到占位符,从ioc里获取bean注入到本实例来
IOC:控制反转:将对象的建立权,由Spring管理. DI(依赖注入):在Spring建立对象的过程当中,把对象依赖的属性注入到类中。
构造器注入 setter方法注入 注解注入 接口注入
Bean在建立的时候能够给该Bean打标,若是递归调用回来发现正在建立中的话,即说明了循环依赖了。
Spring中循环依赖场景有:
一、切面(aspect):类是对物体特征的抽象,切面就是对横切关注点的抽象
二、横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。
三、链接点(joinpoint):被拦截到的点,由于 Spring 只支持方法类型的链接点,因此在Spring 中链接点指的就是被拦截到的方法,实际上链接点还能够是字段或者构造器。
四、切入点(pointcut):对链接点进行拦截的定义
五、通知(advice):所谓通知指的就是指拦截到链接点以后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类。
六、目标对象:代理的目标对象
七、织入(weave):将切面应用到目标对象并致使代理对象建立的过程
八、引入(introduction):在不修改代码的前提下,引入能够在运行期为类动态地添加方法或字段。
传统oop开发代码逻辑自上而下的,这个过程当中会产生一些横切性问题,这些问题与咱们主业务逻辑关系不大,会散落在代码的各个地方,形成难以维护,aop思想就是把业务逻辑与横切的问题进行分离,达到解耦的目的,提升代码重用性和开发效率;
@EnableAspectJAutoProxy给容器(beanFactory)中注册一个AnnotationAwareAspectJAutoProxyCreator对象;
AnnotationAwareAspectJAutoProxyCreator对目标对象进行代理对象的建立,对象内部,是封装JDK和CGlib两个技术,实现动态代理对象建立的(建立代理对象过程当中,会先建立一个代理工厂,获取到全部的加强器(通知方法),将这些加强器和目标类注入代理工厂,再用代理工厂建立对象);
代理对象执行目标方法,获得目标方法的拦截器链,利用拦截器的链式机制,依次进入每个拦截器进行执行
(1):用户请求发送给DispatcherServlet,DispatcherServlet调用HandlerMapping处理器映射器;
(2):HandlerMapping根据xml或注解找到对应的处理器,生成处理器对象返回给DispatcherServlet;
(3):DispatcherServlet会调用相应的HandlerAdapter;
(4):HandlerAdapter通过适配调用具体的处理器去处理请求,生成ModelAndView返回给DispatcherServlet
(5):DispatcherServlet将ModelAndView传给ViewReslover解析生成View返回给DispatcherServlet;
(6):DispatcherServlet根据View进行渲染视图;
->DispatcherServlet->HandlerMapping->Handler ->DispatcherServlet->HandlerAdapter处理handler->ModelAndView ->DispatcherServlet->ModelAndView->ViewReslover->View ->DispatcherServlet->返回给客户
默认状况下一级缓存是开启的,并且是不能关闭的。
new springApplication对象,利用spi机制加载applicationContextInitializer, applicationLister接口实例(META-INF/spring.factories);
调run方法准备Environment,加载应用上下文(applicationContext),发布事件 不少经过lister实现
建立spring容器, refreshContext() ,实现starter自动化配置,spring.factories文件加载, bean实例化
核心注解是@SpringBootApplication 由如下三种组成
spring-boot-starter-web - Web 和 RESTful 应用程序; spring-boot-starter-test - 单元测试和集成测试; spring-boot-starter-jdbc - 传统的 JDBC; spring-boot-starter-security - 使用 SpringSecurity 进行身份验证和受权; spring-boot-starter-data-jpa - 带有 Hibernate 的 Spring Data JPA; spring-boot-starter-data-rest - 使用 Spring Data REST 公布简单的 REST 服务
(1):Application.yml 通常用来定义单个应用级别的,若是搭配 spring-cloud-config 使用
(2).Bootstrap.yml(先加载) 系统级别的一些参数配置,这些参数通常是不变的
(1):zuul则是netflix公司的项目集成在spring-cloud中使用而已, Gateway是spring-cloud的 一个子项目;
(2):zuul不提供异步支持流控等均由hystrix支持, gateway提供了异步支持,提供了抽象负载均衡,提供了抽象流控; 理论上gateway则更适合于提升系统吞吐量(但不必定能有更好的性能),最终性能还须要经过严密的压测来决定
(3):二者底层实现都是servlet,可是gateway多嵌套了一层webflux框架
(4): zuul可用至其余微服务框架中,内部没有实现限流、负载均衡;gateway只能用在springcloud中;
(1):请求给zuulservlet处理(HttpServlet子类) zuulservlet中有一个zuulRunner对象,该对象中初始化了RequestContext(存储请求的数据),RequestContext被全部的zuulfilter共享;
(2): zuulRunner中有 FilterProcessor(zuulfilter的管理器),其从filterloader 中获取zuulfilter;
(3):有了这些filter以后, zuulservelet执行的Pre-> route-> post 类型的过滤器,若是在执行这些过滤器有错误的时候则会执行error类型的过滤器,执行完后把结果返回给客户端.
(1):请求到达DispatcherHandler, DispatchHandler在IOC容器初始化时会在容器中实例化HandlerMapping接口
(2):用handlerMapping根据请求URL匹配到对应的Route,而后有对应的filter作对应的请求转发最终response返回去
Zookeeper 的核心是原子广播,这个机制保证了各个 server 之间的同步。实现这个机制的协议叫作 Zab 协议。Zab 协议有两种模式,它们分别是恢复模式和广播模式。
经过维护一个本身的线程池,当线程池达到阈值的时候,就启动服务降级,返回fallback默认值
防止雪崩,及时释放资源,防止系统发生更多的额级联故障,须要对故障和延迟进行隔离,防止单个依赖关系的失败影响整个应用程序;
ActiveMQ:Apache出品,最先使用的消息队列产品,时间比较长了,最近版本更新比较缓慢。 RabbitMQ:erlang语言开发,支持不少的协议,很是重量级,更适合于企业级的开发。性能较好,可是不利于作二次开发和维护。 RocketMQ:阿里开源的消息中间件,纯Java开发,具备高吞吐量、高可用性、适合大规模分布式系统应用的特色,分布式事务。 ZeroMQ:号称最快的消息队列系统,尤为针对大吞吐量的需求场景,采用 C 语言实现。 消息队列的选型须要根据具体应用需求而定,ZeroMQ 小而美,RabbitMQ 大而稳,Kakfa 和 RocketMQ 快而强劲
(1个字节是8个bit) 整数型:byte(1字节)、short(2字节)、int(4字节)、long(8字节) 浮点型:float(4字节)、double(8字节) 布尔型:boolean(1字节) 字符型:char(2字节)
包括 类File,outputStream,inputStream,writer,readerseralizable(5类1接口)
NIO三大核心内容 selector(选择器,用于监听channel),channel(通道),buffer(缓冲区)
NIO与IO区别,IO面向流,NIO面向缓冲区;io阻塞,nio非阻塞
throwable为父类,子为error跟exception,exception分runtime(空指针,越界等)跟checkexception(sql,io,找不到类等异常)
sleep属于线程类,wait属于object类;sleep不释放锁
线程中建立副本,访问本身内部的副本变量,内部实现是其内部类名叫ThreadLocalMap的成员变量threadLocals,key为自己,value为实际存值的变量副本
若是是强引用,设置tl=null,可是key的引用依然指向ThreadLocal对象,因此会有内存泄漏,而使用弱引用则不会; 可是仍是会有内存泄漏存在,ThreadLocal被回收,key的值变成null,致使整个value再也没法被访问到; 解决办法:在使用结束时,调用ThreadLocal.remove来释放其value的引用;
ThreadLocal是不具有继承性的,因此是没法获取到的,可是咱们能够用InteritableThreadLocal来实现这个功能。InteritableThreadLocal继承来ThreadLocal,重写了createdMap方法,已经对应的get和set方法,不是在利用了threadLocals,而是interitableThreadLocals变量。
这个变量会在线程初始化的时候(调用init方法),会判断父线程的interitableThreadLocals变量是否为空,若是不为空,则把放入子线程中,可是其实这玩意没啥鸟用,当父线程建立完子线程后,若是改变父线程内容是同步不到子线程的。。。一样,若是在子线程建立完后,再去赋值,也是没啥鸟用的
线程池有5种状态:running,showdown,stop,Tidying,TERMINATED。
running:线程池处于运行状态,能够接受任务,执行任务,建立线程默认就是这个状态了
showdown:调用showdown()函数,不会接受新任务,可是会慢慢处理完堆积的任务。
stop:调用showdownnow()函数,不会接受新任务,不处理已有的任务,会中断现有的任务。
Tidying:当线程池状态为showdown或者stop,任务数量为0,就会变为tidying。这个时候会调用钩子函数terminated()。
TERMINATED:terminated()执行完成。
在线程池中,用了一个原子类来记录线程池的信息,用了int的高3位表示状态,后面的29位表示线程池中线程的个数。
若是提交任务的时候使用了submit,则返回的feature里会存有异常信息,可是若是数execute则会打印出异常栈。可是不会给其余线程形成影响。以后线程池会删除该线程,会新增长一个worker。
AbortPolicy直接抛出异常阻止线程运行;
CallerRunsPolicy若是被丢弃的线程任务未关闭,则执行该线程;
DiscardOldestPolicy移除队列最先线程尝试提交当前任务
DiscardPolicy丢弃当前任务,不作处理
contentionList(请求锁线程队列) entryList(有资格的候选者队列) waitSet(wait方法后阻塞队列) onDeck(竞争候选者) ower(竞争到锁线程) !ower(执行成功释放锁后状态); Synchronized 是非公平锁。
Synchronized 在线程进入 ContentionList 时,等待的线程会先尝试自旋获取锁,若是获取不到就进入 ContentionList,这明显对于已经进入队列的线程是不公平的,还有一个不公平的事情就是自旋获取锁的线程还可能直接抢占 OnDeck 线程的锁资源。
底层是由一对monitorenter和monitorexit指令实现的(监视器锁)
每一个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的全部权,过程:
内部自定义了同步器 Sync,加锁的时候经过CAS 算法 ,将线程对象放到一个双向链表 中,每次获取锁的时候 ,看下当前维 护的那个线程ID和当前请求的线程ID是否同样,同样就可重入了;
(1):tryLock 能得到锁就返回 true,不能就当即返回 false,
(2):tryLock(long timeout,TimeUnit unit),能够增长时间限制,若是超过该时间段还没得到锁,返回 false
(3):lock 能得到锁就返回 true,不能的话一直等待得到锁
(4):lock 和 lockInterruptibly,若是两个线程分别执行这两个方法,但此时中断这两个线程, lock 不会抛出异常,而 lockInterruptibly 会抛出异常。
CountDownLatch是等待其余线程执行到某一个点的时候,在继续执行逻辑(子线程不会被阻塞,会继续执行),只能被使用一次。最多见的就是join形式,主线程等待子线程执行完任务,在用主线程去获取结果的方式(固然不必定),内部是用计数器相减实现的(没错,又特么是AQS),AQS的state承担了计数器的做用,初始化的时候,使用CAS赋值,主线程调用await()则被加入共享线程等待队列里面,子线程调用countDown的时候,使用自旋的方式,减1,知道为0,就触发唤醒。
CyclicBarrier回环屏障,主要是等待一组线程到底同一个状态的时候,放闸。CyclicBarrier还能够传递一个Runnable对象,能够到放闸的时候,执行这个任务。CyclicBarrier是可循环的,当调用await的时候若是count变成0了则会重置状态,如何重置呢,CyclicBarrier新增了一个字段parties,用来保存初始值,当count变为0的时候,就从新赋值。还有一个不一样点,CyclicBarrier不是基于AQS的,而是基于RentrantLock实现的。存放的等待队列是用了条件变量的方式。
信号量是一种固定资源的限制的一种并发工具包,基于AQS实现的,在构造的时候会设置一个值,表明着资源数量。信号量主要是应用因而用于多个共享资源的互斥使用,和用于并发线程数的控制(druid的数据库链接数,就是用这个实现的),信号量也分公平和非公平的状况,基本方式和reentrantLock差很少,在请求资源调用task时,会用自旋的方式减1,若是成功,则获取成功了,若是失败,致使资源数变为了0,就会加入队列里面去等待。调用release的时候会加一,补充资源,并唤醒等待队列。
(1):可重入锁是指同一个线程能够屡次获取同一把锁,不会由于以前已经获取过还没释放而阻塞;
(2):reentrantLock和synchronized都是可重入锁
(3):可重入锁的一个优势是可必定程度避免死锁
(1):先经过CAS尝试获取锁, 若是此时已经有线程占据了锁,那就加入AQS队列而且被挂起;
(2): 当锁被释放以后, 排在队首的线程会被唤醒CAS再次尝试获取锁,
(3):若是是非公平锁, 同时还有另外一个线程进来尝试获取可能会让这个线程抢到锁;
(4):若是是公平锁, 会排到队尾,由队首的线程获取到锁。
Node内部类构成的一个双向链表结构的同步队列,经过控制(volatile的int类型)state状态来判断锁的状态,对于非可重入锁状态不是0则去阻塞;
对于可重入锁若是是0则执行,非0则判断当前线程是不是获取到这个锁的线程,是的话把state状态+1,好比重入5次,那么state=5。 而在释放锁的时候,一样须要释放5次直到state=0其余线程才有资格得到锁
Exclusive:独占,只有一个线程能执行,如ReentrantLock
Share:共享,多个线程能够同时执行,如Semaphore、CountDownLatch、ReadWriteLock,CyclicBarrier
内存值V,旧的预期值A,要修改的新值B,当A=V时,将内存值修改成B,不然什么都不作;
(1):ABA问题; (2):若是CAS失败,自旋会给CPU带来压力; (3):只能保证对一个变量的原子性操做,i++这种是不能保证的
(1):Atomic系列
(1):公平锁指在分配锁前检查是否有线程在排队等待获取该锁,优先分配排队时间最长的线程,非公平直接尝试获取锁 (2):公平锁需多维护一个锁线程队列,效率低;默认非公平
(1):ReentrantLock为独占锁(悲观加锁策略) (2):ReentrantReadWriteLock中读锁为共享锁 (3): JDK1.8 邮戳锁(StampedLock), 不可重入锁 读的过程当中也容许获取写锁后写入!这样一来,咱们读的数据就可能不一致,因此,须要一点额外的代码来判断读的过程当中是否有写入,这种读锁是一种乐观锁, 乐观锁的并发效率更高,但一旦有小几率的写入致使读取的数据不一致,须要能检测出来,再读一遍就行
无锁
偏向锁 会偏向第一个访问锁的线程,当一个线程访问同步代码块得到锁时,会在对象头和栈帧记录里存储锁偏向的线程ID,当这个线程再次进入同步代码块时,就不须要CAS操做来加锁了,只要测试一下对象头里是否存储着指向当前线程的偏向锁 若是偏向锁未启动,new出的对象是普通对象(即无锁,有稍微竞争会成轻量级锁),若是启动,new出的对象是匿名偏向(偏向锁) 对象头主要包括两部分数据:Mark Word(标记字段, 存储对象自身的运行时数据)、class Pointer(类型指针, 是对象指向它的类元数据的指针)
轻量级锁(自旋锁) (1):在把线程进行阻塞操做以前先让线程自旋等待一段时间,可能在等待期间其余线程已经 解锁,这时就无需再让线程执行阻塞操做,避免了用户态到内核态的切换。(自适应自旋时间为一个线程上下文切换的时间)
(2):在用自旋锁时有可能形成死锁,当递归调用时有可能形成死锁
(3):自旋锁底层是经过指向线程栈中Lock Record的指针来实现的
重量级锁
(1):轻量级锁是经过CAS来避免进入开销较大的互斥操做
(2):偏向锁是在无竞争场景下彻底消除同步,连CAS也不执行
(1):某线程自旋次数超过10次;
(2):等待的自旋线程超过了系统core数的一半;
经常使用的读写锁ReentrantReanWritelock,这个其实和reentrantLock类似,也是基于AQS的,可是这个是基于共享资源的,不是互斥,关键在于state的处理,读写锁把高16为记为读状态,低16位记为写状态,就分开了,读读状况其实就是读锁重入,读写/写读/写写都是互斥的,只要判断低16位就行了。
(1):利用节点名称惟一性来实现,加锁时全部客户端一块儿建立节点,只有一个建立成功者得到锁,解锁时删除节点。
(2):利用临时顺序节点实现,加锁时全部客户端都建立临时顺序节点,建立节点序列号最小的得到锁,不然监视比本身序列号次小的节点进行等待
(3):方案2比1好处是当zookeeper宕机后,临时顺序节点会自动删除释放锁,不会形成锁等待;
(4):方案1会产生惊群效应(当有不少进程在等待锁的时候,在释放锁的时候会有不少进程就过来争夺锁)。
(5):因为须要频繁建立和删除节点,性能上不如redis锁
(1):变量可见性
(2):防止指令重排序
(3):保障变量单次读,写操做的原子性,但不能保证i++这种操做的原子性,由于本质是读,写两次操做
volatile可见性是有指令原子性保证的,在jmm中定义了8类原子性指令,好比write,store,read,load。而volatile就要求write-store,load-read成为一个原子性操做,这样子能够确保在读取的时候都是从主内存读入,写入的时候会同步到主内存中(准确来讲也是内存屏障),指令重排则是由内存屏障来保证的,由两个内存屏障:
jdk是最小的开发环境,由jre++java工具组成。
jre是java运行的最小环境,由jvm+核心类库组成。
jvm是虚拟机,是java字节码运行的容器,若是只有jvm是没法运行java的,由于缺乏了核心类库。
(1):堆<对象,静态变量,共享
(2):方法区<存放类信息,常量池,共享>(java8移除了永久代(PermGen),替换为元空间(Metaspace))
(3):虚拟机栈<线程执行方法的时候内部存局部变量会存堆中对象的地址等等数据>
(4):本地方法栈<存放各类native方法的局部变量表之类的信息>
(5):程序计数器<记录当前线程执行到哪一条字节码指令位置>
(1):强(内存泄露主因)
(2):软(只有软引用的话,空间不足将被回收),适合缓存用
(3):弱(只,GC会回收)
(4):虚引用(用于跟踪GC状态)用于管理堆外内存
一个对象分为3个区域:对象头、实例数据、对齐填充
对象头:主要是包括两部分,1.存储自身的运行时数据好比hash码,分代年龄,锁标记等(可是不是绝对哦,锁状态若是是偏向锁,轻量级锁,是没有hash码的。。。是不固定的)2.指向类的元数据指针。还有可能存在第三部分,那就是数组类型,会多一块记录数组的长度(由于数组的长度是jvm判断不出来的,jvm只有元数据信息)
实例数据:会根据虚拟机分配策略来定,分配策略中,会把相同大小的类型放在一块儿,并按照定义顺序排列(父类的变量也会在哦)
对齐填充:这个意义不是很大,主要在虚拟机规范中对象必须是8字节的整数,因此当对象不知足这个状况时,就会用占位符填充
通常判断对象是否存活有两种算法,一种是引用计数,另一种是可达性分析。在java中主要是第二种
根据GC ROOTS。GC ROOTS能够的对象有:虚拟机栈中的引用对象,方法区的类变量的引用,方法区中的常量引用,本地方法栈中的对象引用。
(1):加载 获取类的二进制字节流,将其静态存储结构转化为方法区的运行时数据结构
(2):校验 文件格式验证,元数据验证,字节码验证,符号引用验证
(3):准备 在方法区中对类的static变量分配内存并设置类变量数据类型默认的初始值,不包括实例变量,实例变量将会在对象实例化的时候随着对象一块儿分配在Java堆中
(4):解析 将常量池内的符号引用替换为直接引用的过程
(5):初始化 为类的静态变量赋予正确的初始值(Java代码中被显式地赋予的值)
(1):启动类加载器(home) 加载jvm核心类库,如java.lang.*等
(2):扩展类加载器(ext), 父加载器为启动类加载器,从jre/lib/ext下加载类库
(3):应用程序类加载器(用户classpath路径) 父加载器为扩展类加载器,从环境变量中加载类
(1):类加载器收到类加载的请求
(2):把这个请求委托给父加载器去完成,一直向上委托,直到启动类加载器
(3):启动器加载器检查能不能加载,能就加载(结束);不然,抛出异常,通知子加载器进行加载
(4):保障类的惟一性和安全性以及保证JDK核心类的优先加载
保证java基础类在不一样的环境仍是同一个Class对象,避免出现了自定义类覆盖基础类的状况,致使出现安全问题。还能够避免类的重复加载。
(1):自定义类加载器,继承ClassLoader类重写loadClass方法;
(2):SPI
tomcat有着特殊性,它须要容纳多个应用,须要作到应用级别的隔离,并且须要减小重复性加载,因此划分为:/common 容器和应用共享的类信息,/server容器自己的类信息,/share应用通用的类信息,/WEB-INF/lib应用级别的类信息。总体能够分为:boostrapClassLoader->ExtensionClassLoader->ApplicationClassLoader->CommonClassLoader->CatalinaClassLoader(容器自己的加载器)/ShareClassLoader(共享的)->WebAppClassLoader。虽然第一眼是知足双亲委派模型的,可是不是的,由于双亲委派模型是要先提交给父类装载,而tomcat是优先判断是不是本身负责的文件位置,进行加载的。
(1):服务提供接口(服务发现机制):
(2):经过加载ClassPath下META_INF/services,自动加载文件里所定义的类
(3):经过ServiceLoader.load/Service.providers方法经过反射拿到实现类的实例
(1):应用于JDBC获取数据库驱动链接过程就是应用这一机制
(2):apache最先提供的common-logging只有接口.没有实现..发现日志的提供商经过SPI来具体找到日志提供商实现类
(1):双亲委派核心是越基础的类由越上层的加载器进行加载, 基础的类老是做为被调用代码调用的API,没法实现基础类调用用户的代码….
(2):JNDI服务它的代码由启动类加载器去加载,可是他须要调独立厂商实现的应用程序,如何解决? 线程上下文件类加载器(Thread Context ClassLoader), JNDI服务使用这个线程上下文类加载器去加载所须要的SPI代码,也就是父类加载器请求子类加载器去完成类加载动做Java中全部涉及SPI的加载动做基本上都采用这种方式,例如JNDI,JDBC
(1):老年代空间不足
(2):永久代(方法区)空间不足
(3):显式调用system.gc()
Ehcache中的一些版本,各类 NIO 框架,Dubbo,Memcache 等中会用到,NIO包下ByteBuffer来建立堆外内存 堆外内存,其实就是不受JVM控制的内存。
减小了垃圾回收的工做,由于垃圾回收会暂停其余的工做。 加快了复制的速度。由于堆内在 flush 到远程时,会先复制到直接内存(非堆内存),而后在发送;而堆外内存至关于省略掉了复制这项工做。 能够扩展至更大的内存空间。好比超过 1TB 甚至比主存还大的空间。
堆外内存难以控制,若是内存泄漏,那么很难排查,经过-XX:MaxDirectMemerySize来指定,当达到阈值的时候,调用system.gc来进行一次full gc 堆外内存相对来讲,不适合存储很复杂的对象。通常简单的对象或者扁平化的比较适合 jstat查看内存回收概况,实时查看各个分区的分配回收状况, jmap查看内存栈,查看内存中对象占用大小, jstack查看线程栈,死锁,性能瓶颈
(1): Serial 收集器 复制算法,单线程,新生代)
(2): ParNew 收集器(复制算法,多线程,新生代)
(3): Parallel Scavenge 收集器(多线程,复制算法,新生代,高吞吐量)
(4):Serial Old 收集器(标记-整理算法,老年代)
(5):Parallel Old 收集器(标记-整理算法,老年代,注重吞吐量的场景下,jdk8默认采用 Parallel Scavenge + Parallel Old 的组合)
(6):CMS 收集器(标记-清除算法,老年代,垃圾回收线程几乎能作到与用户线程同时工做,吞吐量低,内存碎片)以牺牲吞吐量为代价来得到最短回收停顿时间-XX:+UseConcMarkSweepGC jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代) jdk1.9 默认垃圾收集器G1
(1):应用程序对停顿比较敏感
(2):在JVM中,有相对较多存活时间较长的对象(老年代比较大)会更适合使用CMS
(1):初始标识<找到gcroot(stw)>
1:系统类加载器加载的对象
2:处于激活状态的线程
3:JNI栈中的对象
4:正在被用于同步的各类锁对象
5:JVM自身持有的对象,好比系统类加载器等。
(2):并发标记(三色标记算法) 三色标记算法处理并发标记出现对象引用变化状况: 黑:本身+子对象标记完成 灰:本身完成,子对象未完成 白:未标记; 并发标记 黑->灰->白 从新标记 灰->白引用消失,黑引用指向->白,致使白漏标 cms处理办法是incremental update方案 (增量更新)把黑色变成灰色 多线程下并发标记依旧会产生漏标问题,因此cms必须remark一遍(jdk1.9之后不用cms了)
SATB(snapshot at the begining)把白放入栈中,标记过程是和应用程序并发运行的(不须要Stop-The-World) 这种方式会形成某些是垃圾的对象也被当作是存活的,因此G1会使得占用的内存被实际须要的内存大。不过下一次就回收了 ZGC 处理方案: 颜色指针(color pointers) 2*42方=4T
(3):从新标记(stw)
(4)并发清理
备注:从新标记是防止标记成垃圾以后,对象被引用
(5):G1 收集器(新生代 + 老年代,在多 CPU 和大内存的场景下有很好的性能) G1在java9 即是默认的垃圾收集器,是cms 的替代者 逻辑分代,用分区(region)的思想(默认分2048份) 仍是有stw 为解决CMS算法产生空间碎片HotSpot提供垃圾收集器,经过-XX:+UseG1GC来启用
(1):young gc(eden region被耗尽没法申请内存时,就会触发)
(2):mixed gc(当老年代大小占整个堆大小百分比达到该阈值时,会触发)
(3):full gc(对象内存分配速度过快,mixed gc来不及回收,致使老年代被填满,就会触发)
(8):ZGC和shenandoah (oracle产收费) no stw
(1):dashboard命令查看整体jvm运行状况
(2):jvm显示jvm详细信息
(3):thread 显示jvm里面全部线程信息(相似于jstack) 查看死锁线程命令thread -b
(4):sc * 显示全部类(search class)
(5):trace 跟踪方法
第一步:jps获取进程号 第二步:jmap -histo pid | head -20 得知有个对象在不断建立 备注:jmap若是线上服务器堆内存特别大,,会卡死需堆转存(通常会说在测试环境压测,导出转存) -XX:+HeapDumpOnOutOfMemoryError或jmap -dumpLformat=b,file=xxx pid 转出文件进行分析 (arthas没有实现jmap命令)heapdump --live /xxx/xx.hprof导出文件
回收过程 (1):young gc(年轻代回收)--当年轻代的Eden区用尽时--stw 第一阶段,扫描根。 根是指static变量指向的对象,正在执行的方法调用链条上的局部变量等 第二阶段,更新RS(Remembered Sets)。 处理dirty card queue中的card,更新RS。此阶段完成后,RS能够准确的反映老年代对所在的内存分段中对象的引用 第三阶段,处理RS。 识别被老年代对象指向的Eden中的对象,这些被指向的Eden中的对象被认为是存活的对象。 第四阶段,复制对象。 此阶段,对象树被遍历,Eden区内存段中存活的对象会被复制到Survivor区中空的内存分段 第五阶段,处理引用。 处理Soft,Weak,Phantom,Final,JNI Weak 等引用。
(2):concrruent marking(老年代并发标记) 当堆内存使用达到必定值(默认45%)时,不须要Stop-The-World,在并发标记前先进行一次young gc
(3):混合回收(mixed gc) 并发标记过程结束之后,紧跟着就会开始混合回收过程。混合回收的意思是年轻代和老年代会同时被回收
(4):Full GC? Full GC是指上述方式不能正常工做,G1会中止应用程序的执行,使用单线程的内存回收算法进行垃圾回收,性能会很是差,应用程序停顿时间会很长。要避免Full GC的发生,一旦发生须要进行调整。
好比堆内存过小,当G1在复制存活对象的时候没有空的内存分段可用,则会回退到full gc,这种状况能够经过增大内存解决
尽管G1堆内存仍然是分代的,可是同一个代的内存再也不采用连续的内存结构
年轻代分为Eden和Survivor两个区,老年代分为Old和Humongous两个区
新分配的对象会被分配到Eden区的内存分段上
Humongous区用于保存大对象,若是一个对象占用的空间超过内存分段Region的一半;
若是对象的大小超过一个甚至几个分段的大小,则对象会分配在物理连续的多个Humongous分段上。
Humongous对象由于占用内存较大而且连续会被优先回收
为了在回收单个内存分段的时候没必要对整个堆内存的对象进行扫描(单个内存分段中的对象可能被其余内存分段中的对象引用)引入了RS数据结构。RS使得G1能够在年轻代回收的时候没必要去扫描老年代的对象,从而提升了性能。每个内存分段都对应一个RS,RS保存了来自其余分段内的对象对于此分段的引用
JVM会对应用程序的每个引用赋值语句object.field=object进行记录和处理,把引用关系更新到RS中。可是这个RS的更新并非实时的。G1维护了一个Dirty Card Queue
这是为了性能的须要,使用队列性能会好不少。
栈上分配->tlab->堆上分配 因为堆内存是应用程序共享的,应用程序的多个线程在分配内存的时候须要加锁以进行同步。为了不加锁,提升性能每个应用程序的线程会被分配一个TLAB。TLAB中的内存来自于G1年轻代中的内存分段。当对象不是Humongous对象,TLAB也能装的下的时候,对象会被优先分配于建立此对象的线程的TLAB中。这样分配会很快,由于TLAB隶属于线程,因此不须要加锁
G1会在年轻代回收过程当中把Eden区中的对象复制(“提高”)到Survivor区中,Survivor区中的对象复制到Old区中。G1的回收过程是多线程执行的,为了不多个线程往同一个内存分段进行复制,那么复制的过程也须要加锁。为了不加锁,G1的每一个线程都关联了一个PLAB,这样就不须要进行加锁了
(1):jmap -heap 10765如上图,能够查看新生代,老生代堆内存的分配大小以及使用状况;
(2):jstat 查看GC收集状况
(3):jmap -dump:live,format=b,file=到本地
(4):经过MAT工具打开分析
(1):生产者(Provider)启动,向注册中心(Register)注册
(2):消费者(Consumer)订阅,然后注册中心通知消费者
(3):消费者从生产者进行消费
(4):监控中心(Monitor)统计生产者和消费者
推荐使用Hessian序列化,还有Duddo、FastJson、Java自带序列化
默认使用 Netty 框架,也是推荐的选择,另外内容还集成有Mina、Grizzly。
(1):随机调用<默认>
(2):权重轮询
(3):最少活跃数
(4):一致性Hash
(1)消费者调用须要消费的服务,
(2):客户端存根将方法、入参等信息序列化发送给服务端存根
(3):服务端存根反序列化操做根据解码结果调用本地的服务进行相关处理
(4):本地服务执行具体业务逻辑并将处理结果返回给服务端存根
(5):服务端存根序列化
(6):客户端存根反序列化
(7):服务消费方获得最终结果
RPC框架的实现目标PC框架的实现目标是把调用、编码/解码的过程给封装起来,让用户感受上像调用本地服务同样的调用远程服务
(1):纯内存操做,避免大量访问数据库,减小直接读取磁盘数据,redis将数据储存在内存里面,读写数据的时候都不会受到硬盘 I/O 速度的限制,因此速度快
(2):单线程操做,避免了没必要要的上下文切换和竞争条件,也不存在多进程或者多线程致使的切换而消耗CPU,不用去考虑各类锁的问题,不存在加锁释放锁操做,没有由于可能出现死锁而致使的性能消耗
(3):采用了非阻塞I/O多路复用机制
(1)Simple dynamic string(SDS)的数据结构
struct sdshdr{
//记录buf数组中已使用字节的数量
//等于 SDS 保存字符串的长度
int len;
//记录 buf 数组中未使用字节的数量
int free;
//字节数组,用于保存字符串
char buf[];
}
复制代码
它的优势: (1)不会出现字符串变动形成的内存溢出问题
(2)获取字符串长度时间复杂度为1
(3)空间预分配, 惰性空间释放free字段,会默认留够必定的空间防止屡次重分配内存
应用场景: String 缓存结构体用户信息,计数
数组+链表的基础上,进行了一些rehash优化; 1.Reids的Hash采用链地址法来处理冲突,而后它没有使用红黑树优化。
2.哈希表节点采用单链表结构。
3.rehash优化 (采用分而治之的思想,将庞大的迁移工做量划分到每一次CURD中,避免了服务繁忙)
应用场景: 保存结构体信息可部分获取不用序列化全部字段
应用场景: (1):好比twitter的关注列表,粉丝列表等均可以用Redis的list结构来实现
(2):list的实现为一个双向链表,便可以支持反向查找和遍历
内部实现是一个 value为null的HashMap,实际就是经过计算hash的方式来快速排重的,这也是set能提供判断一个成员 是否在集合内的缘由。 应用场景: 去重的场景,交集(sinter)、并集(sunion)、差集(sdiff),实现如共同关注、共同喜爱、二度好友等功能
内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是全部的成员,排序依据是HashMap里存的score,使用跳跃表的结构能够得到比较高的查找效率,而且在实现上比较简单。 跳表:每一个节点中维持多个指向其余节点的指针,从而达到快速访问节点的目的 应用场景: 实现延时队列
(1):Multi开启事务
(2):Exec执行事务块内命令
(3):Discard 取消事务
(4):Watch 监视一个或多个key,若是事务执行前key被改动,事务将打断
(1):全部命令都将会被串行化的顺序执行,事务执行期间,Redis不会再为其它客户端的请求提供任何服务,从而保证了事物中的全部命令被原子的执行。
(2):Redis事务中若是有某一条命令执行失败,其后的命令仍然会被继续执行
(3):在事务开启以前,若是客户端与服务器之间出现通信故障并致使网络断开,其后全部待执行的语句都将不会被服务器执行。然而若是网络中断事件是发生在客户端执行EXEC命令以后,那么该事务中的全部命令都会被服务器执行
(4):当使用Append-Only模式时,Redis会经过调用系统函数write将该事务内的全部写操做在本次调用中所有写入磁盘。
然而若是在写入的过程当中出现系统崩溃,如电源故障致使的宕机,那么此时也许只有部分数据被写入到磁盘,而另一部分数据却已经丢失。
Redis服务器会在从新启动时执行一系列必要的一致性检测,一旦发现相似问题,就会当即退出并给出相应的错误提示。此时,咱们就要充分利用Redis工具包中提供的redis-check-aof工具,该工具能够帮助咱们定位到数据不一致的错误,并将已经写入的部分数据进行回滚。修复以后咱们就能够再次从新启动Redis服务器了
(1):全量拷贝, 1.slave第一次启动时,链接Master,发送PSYNC命令,
2.master会执行bgsave命令来生成rdb文件,期间的全部写命令将被写入缓冲区。
3. master bgsave执行完毕,向slave发送rdb文件 复制代码
slave收到rdb文件,丢弃全部旧数据,开始载入rdb文件
rdb文件同步结束以后,slave执行从master缓冲区发送过来的因此写命令。
(2):增量拷贝 若是出现网络闪断或者命令丢失等异常状况,从节点以前保存了自身已复制的偏移量和主节点的运行ID
主节点根据偏移量把复制积压缓冲区里的数据发送给从节点,保证主从复制进入正常状态。
(1) Master最好不要作任何持久化工做,如RDB内存快照和AOF日志文件
(2) 若是数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和链接的稳定性,Master和Slave最好在同一个局域网内
(4) 尽可能避免在压力很大的主库上增长从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…这样的结构方便解决单点故障问题,实现Slave对Master的替换。若是Master挂了,能够马上启用Slave1作Master,其余不变。
(1):官方cluster方案
(2):twemproxy
代理方案twemproxy是一个单点,很容易对其形成很大的压力,因此一般会结合keepalived来实twemproy的高可用
(3):codis 基于客户端来进行分片
(1):master挂掉,且当前master没有slave
(2):集群超过半数以上master挂掉,不管是否有slave集群进入fail状态
(1):会话缓存session cache
(2):排行榜/计数器ZRANGE
(3):发布/订阅
(1):先进先出算法(FIFO)
(2):最近使用最少Least Frequently Used(LFU)
(3):最长时间未被使用的Least Recently Used(LRU)
当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操做会致使LRU命中率急剧降低,缓存污染状况比较严重
(1):惰性删除,cpu友好,可是浪费cpu资源
(2):定时删除(不经常使用)
(3):按期删除,cpu友好,节省空间
同一时刻大量缓存失效;
(1):缓存数据增长过时标记
(2):设置不一样的缓存失效时间
(3):双层缓存策略C1为短时间,C2为长期
(4):定时更新策略
频繁请求查询系统中不存在的数据致使;
(1):cache null策略,查询反馈结果为null仍然缓存这个null结果,设置不超过5分钟过时时间
(2):布隆过滤器,全部可能存在的数据映射到足够大的bitmap中 google布隆过滤器:基于内存,重启失效不支持大数据量,没法在分布式场景 redis布隆过滤器:可扩展性,不存在重启失效问题,须要网络io,性能低于google
(1):数据结构使用不合理bigkey
(2):CPU饱和
(3):持久化阻塞,rdb fork子线程,aof每秒刷盘等
(1):使用本地缓存
(2): 利用分片算法的特性,对key进行打散处理(给hot key加上前缀或者后缀,把一个hotkey 的数量变成 redis 实例个数N的倍数M,从而由访问一个 redis key 变成访问 N * M 个redis key)
2.6版本之后lua脚本保证setnx跟setex进行原子性(setnx以后,未setex,服务挂了,锁不释放) a获取锁,超过过时时间,自动释放锁,b获取到锁执行,a代码执行完remove锁,a和b是同样的key,致使a释放了b的锁。 解决办法:remove以前判断value(高并发下value可能被修改,应该用lua来保证原子性)
bgsave作镜像全量持久化,aof作增量持久化。由于bgsave会耗费较长时间,不够实时,在停机的时候会致使大量丢失数据 ,因此须要aof来配合使用。在redis实例重启时,会使用bgsave持久化文件从新构建内存,再使用aof重放近期的操做指令来 实 现完整恢复重启以前的状态。
取决于aof日志sync属性的配置,若是不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据。可是在高性能的要求下每次都sync是不现实的,通常都使用定时sync,好比1s1次,这个时候最多就会丢失1s的数据.
(1):基于redis的redission分布式可重入锁RLock,以及配合java集合中lock;
(2):Redission 内部提供了一个监控锁的看门狗,不断延长锁的有效期,默认检查锁的超时时间是30秒
(3):此方案的问题:若是你对某个redis master实例,写入了myLock这种锁key的value,此时会异步复制给对应的master ,slave实例。可是这个过程当中一旦发生redis master宕机,主备切换,redis slave变为了redis master。
接着就会致使,客户端2来尝试加锁的时候,在新的redis master上完成了加锁,而客户端1也觉得本身成功加了锁。 此时就会致使多个客户端对一个分布式锁完成了加锁 解决办法:只须要将新的redis实例,在一个TTL时间内,对客户端不可用便可,在这个时间内,全部客户端锁将被失效或者自动释放.
fork和cow。fork是指redis经过建立子进程来进行bgsave操做,cow指的是copy on write,子进程建立后,父子进程共享数据段,父进程继续提供读写服务,写进的页面数据会逐渐和子进程分离开来。
(1):R文件格式紧凑,方便数据恢复,保存rdb文件时父进程会fork出子进程由其完成具体持久化工做,最大化redis性能,恢复大数据集速度更快,只有手动提交save命令或关闭命令时才触发备份操做;
(2):A记录对服务器的每次写操做(默认1s写入一次),保存数据更完整,在redis重启是会重放这些命令来恢复数据,操做效率高,故障丢失数据更少,可是文件体积更大;
使用keys指令能够扫出指定模式的key列表。 若是这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题? redis的单线程的。keys指令会致使线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候能够使用scan指令,scan指令能够无阻塞的提取出指定模式的key列表,可是会有必定的重复几率,在客户端作一次去重就能够了 ,可是总体所花费的时间会比直接用keys指令长。
通常使用list结构做为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。
使用pub/sub主题订阅者模式,能够实现1:N的消息队列。
在消费者下线的状况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。
使用sortedset,想要执行时间的时间戳做为score,消息内容做为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒以前的数据轮询进行处理。
(1):skiplist的复杂度和红黑树同样,并且实现起来更简单。
(2):在并发环境下红黑树在插入和删除时须要rebalance,性能不如跳表。
一: 确保每列的原子性
二:非主键列不存在对主键的部分依赖 (要求每一个表只描述一件事情)
三: 知足第二范式,而且表中的列不存在对非主键列的传递依赖
(1):主库db的更新事件(update、insert、delete)被写到binlog
(2):主库建立一个binlog dump thread线程,把binlog的内容发送到从库
(3):从库建立一个I/O线程,读取主库传过来的binlog内容并写入到relay log.
(4):从库还会建立一个SQL线程,从relay log里面读取内容写入到slave的db.
(1):异步复制(默认) 主库写入binlog日志后便可成功返回客户端,无须等待binlog日志传递给从库的过程,可是一旦主库宕机,就有可能出现丢失数据的状况。
(2)半同步复制:( 5.5版本以后) (安装半同步复制插件)确保从库接收完成主库传递过来的binlog内容已经写入到本身的relay log(传送log)后才会通知主库上面的等待线程。若是等待超时,则关闭半同步复制,并自动转换为异步复制模式,直到至少有一台从库通知主库已经接收到binlog信息为止
(1):Myiasm是mysql默认的存储引擎,不支持数据库事务,行级锁,外键;插入更新需锁表,效率低,查询速度快,Myisam使用的是非汇集索引
(2):innodb 支持事务,底层为B+树实现,适合处理多重并发更新操做,普通select都是快照读,快照读不加锁。InnoDb使用的是汇集索引
(1):汇集索引就是以主键建立的索引
(2):每一个表只能有一个聚簇索引,由于一个表中的记录只能以一种物理顺序存放,实际的数据页只能按照一颗 B+ 树进行排序
(3):表记录的排列顺序和与索引的排列顺序一致
(4):汇集索引存储记录是物理上连续存在
(5):聚簇索引主键的插入速度要比非聚簇索引主键的插入速度慢不少
(6):聚簇索引适合排序,非聚簇索引不适合用在排序的场合,由于聚簇索引叶节点自己就是索引和数据按相同顺序放置在一块儿,索引序便是数据序,数据序便是索引序,因此很快。非聚簇索引叶节点是保留了一个指向数据的指针,索引自己固然是排序的,可是数据并未排序,数据查询的时候须要消耗额外更多的I/O,因此较慢
(7):更新汇集索引列的代价很高,由于会强制innodb将每一个被更新的行移动到新的位置
(1):除了主键之外的索引
(2):汇集索引的叶节点就是数据节点,而非聚簇索引的叶节点仍然是索引节点,并保留一个连接指向对应数据块
(3):聚簇索引适合排序,非聚簇索引不适合用在排序的场合
(4):汇集索引存储记录是物理上连续存在,非汇集索引是逻辑上的连续。
使用聚簇索引找到包含第一个值的行后,即可以确保包含后续索引值的行在物理相邻
在聚簇索引中不要包含常常修改的列,由于码值修改后,数据行必须移动到新的位置,索引此时会重排,会形成很大的资源浪费
优先使用用户自定义主键做为主键,若是用户没有定义主键,则选取一个Unique键做为主键,若是表中连Unique键都没有定义的话,则InnoDB会为表默认添加一个名为row_id隐藏列做为主键。
每一个表你最多能够创建249个非聚簇索引。非聚簇索引须要大量的硬盘空间和内存
(1):BTree索引可能须要屡次运用折半查找来找到对应的数据块 (2):HASH索引是经过HASH函数,计算出HASH值,在表中找出对应的数据 (3):大量不一样数据等值精确查询,HASH索引效率一般比B+TREE高 (4):HASH索引不支持模糊查询、范围查询和联合索引中的最左匹配规则,而这些Btree索引都支持
(1):须要查询,排序,分组和联合操做的字段适合创建索引
(2):索引多,数据更新表越慢,尽可能使用字段值不重复比例大的字段做为索引,联合索引比多个独立索引效率高
(3):对数据进行频繁查询进创建索引,若是要频繁更改数据不建议使用索引
(4):当对表中的数据进行增长、删除和修改的时候,索引也要动态的维护,下降了数据的维护速度。
(1):B+Tree非叶子节点只存储键值信息,下降B+Tree的高度,全部叶子节点之间都有一个链指针,数据记录都存放在叶子节点中
(2): 红黑树这种结构,h明显要深的多,效率明显比B-Tree差不少
(3):B+树也存在劣势,因为键会重复出现,所以会占用更多的空间。可是与带来的性能优点相比,空间劣势每每能够接受,所以B+树的在数据库中的使用比B树更加普遍
(1):条件是or,若是还想让or条件生效,给or每一个字段加个索引
(2):like开头%
(3):若是列类型是字符串,那必定要在条件中将数据使用引号引用起来,不然不会使用索引
(4):where中索引列使用了函数或有运算
ACID 原子性,一致性,隔离性,永久性
(1):经过预写日志方式实现的,redo和undo机制是数据库实现事务的基础
(2):redo日志用来在断电/数据库崩溃等情况发生时重演一次刷数据的过程,把redo日志里的数据刷到数据库里,保证了事务 的持久性(Durability)
(3):undo日志是在事务执行失败的时候撤销对数据库的操做,保证了事务的原子性
(1):读未提交read-uncommitted-- 脏,不可重复读--幻读 A读取了B未提交的事务,B回滚,A 出现脏读;
(2):不可重复读read-committed-- 不可重复读--幻读 A只能读B已提交的事务,可是A还没结束,B又更新数据隐式提交,而后A又读了一次出现不可重复读;
(3):可重复读repeatable-read<默认>-- 幻读 事务开启,不容许其余事务的UPDATE修改操做 A读取B已提交的事务,然而B在该表插入新的行,以后A在读取的时候多出一行,出现幻读;
(4):串行化serializable--
(1)Propagation.REQUIRED<默认> 若是当前存在事务,则加入该事务,若是当前不存在事务,则建立一个新的事务。
(2)Propagation.SUPPORTS 若是当前存在事务,则加入该事务;若是当前不存在事务,则以非事务的方式继续运行。
(3)Propagation.MANDATORY 若是当前存在事务,则加入该事务;若是当前不存在事务,则抛出异常。
(4)Propagation.REQUIRES_NEW 从新建立一个新的事务,若是当前存在事务,延缓当前的事务。
(5)Propagation.NOT_SUPPORTED 以非事务的方式运行,若是当前存在事务,暂停当前的事务。
(6)Propagation.NEVER 以非事务的方式运行,若是当前存在事务,则抛出异常。
(7)Propagation.NESTED 若是没有,就新建一个事务;若是有,就在当前事务中嵌套其余事务。
(1):互斥: 资源x的任意一个时刻只能被一个线程持有 (2):占有且等待:线程1占有资源x的同时等待资源y,并不释放x (3):不可抢占:资源x一旦被线程1占有,其余线程不能抢占x (4):循环等待:线程1持有x,等待y,线程2持有y,等待x 当所有知足时才会死锁
内容过于硬核了,致使不少排版细节,我没办法作得像其余期同样精致了,你们见谅。
涉及的内容和东西太多了,可能不少都是点到为止,也有不少不全的,也有不少错误的点,已经快3W字了,我校验实在困难,我会放在GitHub上面,你们能够跟我一块儿更新这个文章,造福后人吧。
搞很差下次我须要看的时候,我都得看着这个复习了。
我是敖丙,一个在互联网苟且偷生的工具人。
你知道的越多,你不知道的越多,人才们的 【三连】 就是丙丙创做的最大动力,咱们下期见!
注:若是本篇博客有任何错误和建议,欢迎人才们留言,你快说句话啊!
文章持续更新,能够微信搜索「 三太子敖丙 」第一时间阅读,回复【资料】【面试】【简历】有我准备的一线大厂面试资料和简历模板,本文 GitHub github.com/JavaFamily 已经收录,有大厂面试完整考点,欢迎Star。