Hashcode与equals区别与关系(都继承自Object类):

Hashcode:用来获取hash值 返回的是一个int整数 这个hash值是用来确定该对象赛hash表中索引的位置

Equeals:如果重写了equals对象 对比的是内容是否相同 如果没有重写 则比较两个值的地址值是否相同

equals()相等的两个对象,hashcode()一定相等;
反过来:hashcode()不等,一定能推出equals()也不等;
hashcode()相等,equals()可能相等,也可能不等

Java中线程池有哪些:

1.newCachedThreadPool创建一个可缓存线程池程 如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程

2.newFixedThreadPool 创建一个定长线程池 每当提交一个任务就创建一个工作线程,当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列(没有大小限制)中 由于该线程池只有核心线程并且不会被回收 所以可以更加迅速的响应外部请求

3.newScheduledThreadPool 创建一个定长线程池 核心线程数量是固定的 而非核心线程数量无限制 当非线程池闲置时会立即被回收 它可安排给定延迟后运行命令或者定期地执行。这类线程池主要用于执行定时任务和具有固定周期的重复任务。

4.newSingleThreadExecutor 创建一个单线程化的线程池 只有一个核心线程 以无界队列方式来执行该线程,这使得这些任务之间不需要处理线程同步的问题,它确保所有的任务都在同一个线程中按顺序中执行,并且可以在任意给定的时间不会有多个线程是活动的。

Springsecurity中用一个方法一步就可以将自定义的过滤器引入:
只需要实现attemptAuthentication方法即可

Jdk1.8后出现的迭代器:
Spliterator:java1.8引入的一种并行遍历机制

Rabbitmq死信队列:(当消息成为死信后 可以被重新发布到另一个交换机(exchange)这个交换机就是死信队列)
消息变成死信的情况:
消息被拒绝(basic.reject / basic.nack),并且requeue = false
消息TTL过期
队列达到最大长度
死信处理过程

  1. DLX也是一个正常的Exchange,和一般的Exchange没有区别,它能在任何的队列上被指定,实际上就是设置某个队列的属性。
  2. 当这个队列中有死信时,RabbitMQ就会自动的将这个消息重新发布到设置的Exchange上去,进而被路由到另一个队列。
  3. 可以监听这个队列中的消息做相应的处理。

Freemarker缓存是怎么处理的:

FreeMarker 的缓存处理主要用于模版文件的缓存,一般来讲,模版文件改动不会很频繁,在一个流量非常大的网站中,如果频繁的读取模版文件对系统的负担还是很重的,因此 FreeMarker 通过将模版文件的内容进行缓存,来降低模版文件读取的频次,降低系统的负载。
当处理某个模版时,FreeMarker直接从缓存中返回对应的 Template 对象,并有一个默认的机制来保证该模版对象是跟模版文件同步的。如果使用的时候 FreemarkerServlet 时,有一个配置项template_update_delay用来指定更新模版文件的间隔时间,相当于多长时间检测一下是否有必要重新加载模版文件,0 表示每次都重新加载,否则为多少毫秒钟检测一下模版是否更改。
FreeMarker定义了一个统一的缓存处理接口CacheStorage,默认的实现是 MruCacheStorage 最近最少使用的缓存策略。一般情况下,很少需要对缓存进行扩展处理。您可以通过下面的代码指定最大缓存的模版数:

1 cfg.setCacheStorage(new freemarker.cache.MruCacheStorage(20, 250))
其中第一个参数是最大的强引用对象数,第二个为最大的弱引用对象数。这两个值FreeMarker默认的是0和 Integer.MAX_VALUE,表明模版缓存数是无限的。

当业务执行中 消费方去下载文件过程失败了怎么办 比如我手动在GridFS把文件删了,前端已经收到生产方成功得消息了

解决:
ACK机制:
由于通信过程的不可靠性,传输的数据不可避免的会出现丢失、延迟、错误、重复等各种状况,TCP协议为解决这些问题设计了一系列机制。这个机制的核心,就是发送方向接收方发送数据后,接收方要向发送方发送ACK(回执信息)。如果发送方没接收到正确的ACK,就会重新发送数据直到接收到正确ACK为止。比如:发送方发送的数据序号是seq,那么接收方会发送seq + 1作为ACK,这样发送方就知道接下来要发送序号为seq + 1的数据给接收方了 如下图:

应用场景:
(1)数据丢失或延迟。发送方发送数据seq时会起一个定时器,如果在指定时间内没有接收到ACK seq + 1,就把数据seq再发一次。
(2)数据乱序。接收方上一个收到的正确数据是seq + 4,它返回seq + 5作为ACK。这时候它收到了seq + 7,因为顺序错了,所以接收方会再次返回seq + 5给发送方。
(3)数据错误。每一个TCP数据都会带着数据的校验和。接收方收到数据seq + 3以后会先对校验和进行验证。如果结果不对,则发送ACK seq + 3,让发送方重新发送数据。
(4)数据重复。接收方直接丢弃重复的数据即可。

懒汉模式和饿汉模式的区别

懒汉模式:在类加载的时候不被初始化。
饿汉模式:在类加载时就完成了初始化,但是加载比较慢,获取对象比较快。
饿汉模式是线程安全的,在类创建好一个静态对象提供给系统使用,懒汉模式在创建对象时不加上synchronized,会导致对象的访问不是线程安全的
多线程下如何实现单例模式:
由上可知 饿汉模式 类加载时就完成初始化 提前创建好实例 影响加载效率 线程安全
懒汉模式则 类加载时不需要提前创建好实例,加载效率较快,需要安全处理,否则线程不安全
懒汉模式安全处理方案:
(1)在方法区上加锁 但是运行效率低下 下一个线程调用同步方法必须等待上一个释放锁执行完毕

(2)同步代码快加锁 但是将全部代码锁上了 所以跟上面一样 效率低下

(3)在需要同步执行的代码块加锁,不影响执行效率且线程安全。

(4)双检查锁Double Check Locking
这里在声明变量时使用了volatile关键字来保证其线程间的可见性(还可以防止指令重排序:只有Happens-Before才会出现这样的指令重排问题 所以解决:可以在每个volatile关键字写操作前加一个StoreStore屏障;在每个volatile写操作后面插入一个StoreLoad屏障;在每个volatile读操作后面插入一个LoadLoad屏障;在每个volatile读操作后面插入一个LoadStore屏障);在同步代码块中使用二次检查,以保证其不被重复实例化。集合其二者,这种实现方式既保证了其高效性,也保证了其线程安全性

Volatile关键字:

(4)静态内部类实现:

(5)静态代码块实现:

(6)内部枚举类实现:

Spring

是一个轻量级IOC和AOP容器框架 用于简化企业级应用程序开发 常见的配置方式有三种:基于XML的配置、基于注解的配置、基于Java的配置
优点:
(1)入侵式设计 代码污染低
(2)DI机制将对象间的依赖管理交给框架处理 减轻组件耦合性
(3)提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用
(4)对于主流应用框架提供集成支持
Spring Aop的理解:
OOP面向对象,允许开发者定义纵向的关系,但并适用于定义横向的关系,导致了大量代码的重复,而不利于各个模块的重用
AOP,一般称为面向切面,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理

Aop实现:

关键在于 代理模式 AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表
(1)AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
①JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
②如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
(3)静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):proxy是最终生成的代理实例; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。

Spring的IoC理解:

(1)IOC就是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。DI依赖注入,和控制反转是同一个概念的不同角度的描述,即 应用程序在运行时依赖IoC容器来动态注入对象需要的外部资源。
(2)最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,使用java的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。
(3)Spring的IOC有三种注入方式 :构造器注入、setter方法注入、根据注解注入。
IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。

解决线程安全问题:

(1)同步代码块:
在代码块声明上 加上synchronized
synchronized (锁对象) {
可能会产生线程安全问题的代码
}
同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。
(2)同步方法:
在方法声明上加上synchronized
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步方法中的锁对象是 this
静态同步方法: 在方法声明上加上static synchronized
静态同步方法中的锁对象是 类名.class
(3)同步锁
Lock接口提供了与synchronized关键字类似的同步功能,但需要在使用时手动获取锁和释放锁。
补充:
异步锁:当多个jvm或者服务器 操作同一个变量时 会出现线程安全问题 需要异步锁进行处理:
(1)数据库的乐观锁 悲观锁 不推荐 容易造成锁表 死锁
(2)Redis分布式锁 设置一个flag标识 当一个服务拿到锁之后立即将标识设置为false 用完后释放锁 并且将标识设置为true

(3)使用dubbo zookeeper (共享锁,排它锁),这里就根据自己的情况,共享锁还是会出现阻塞的情况,排它锁就是会生成很多临时的节点,谁先获取最小的序号标识谁就先获取到锁。

Springmvc执行流程:

Springboot自动配置原理:

SpringBoot启动的时候加载启动类,@EnableAutoConfiguration注解开启了自动配置功能 @EnableAutoConfiguration的作用是利用AutoConfigurationImportSelector给容器中导入一些组件查看selectImports()方法的内容该方法中的 getCandidateConfigurations()方法获取候选的配置 内部的loadFactoryNames()方法扫描所有jar包类路径下 META‐INF/spring.factories把扫描到的这些文件的内容包装成properties对象 从properties中获取到EnableAutoConfiguration.class类名对应的值,然后把他们添加在容器中(整个过程就是将类路径下"META-INF/spring.factories"里面配置的所有EnableAutoConfiguration的值加入到容器中)
每一个这样的 xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中;用他们来做自动配置;每一个自动配置类进行自动配置功能
以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理:
根据@Conditional指定的条件判断,决定这个配置类是否生效 一但这个配置类生效;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;所有在配置文件中能配置的属性都是在xxxxProperties类中封装者;配置文件能配置什么就可以参照某个功能对应的这个属性类
类加载:(类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class对象,用来封装类在方法区内的数据结构)
类加载器有哪些:(类加载器采用双亲委派模型)
类加载器的三大特性:委托性、可见性、单一性

1.启动类加载器:这个类加载器负责放在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库。用户无法直接使用。不是ClassLoader的子类 其他两个是
2.扩展类加载器:这个类加载器由sun.misc.LauncherKaTeX parse error: Undefined control sequence: \lib at position 32: …。它负责<JAVA_HOME>\̲l̲i̲b̲\ext目录中的,或者被jav…AppClassLoader实现。是ClassLoader中getSystemClassLoader()方法的返回值。它负责用户路径(ClassPath)所指定的类库。用户可以直接使用。如果用户没有自己定义类加载器,默认使用这个。
4.自定义加载器:用户自己定义的类加载器。
1:类的加载过程:
当使用Java命令运行java程序时 此时jvm(虚拟机)启动 并去方法区下寻找java命令后面跟的类是否存在 若不存在 则将这个类加载到方法区下
当类加载到方法区后 会分为两部分执行:先加载非静态内容到非静态方法区域 再加载静态内容到静态方法区域内 当非静态内容加载完后 就会加载所有的静态内容到方法区下的静态区域内
当所有静态内容加载完后 对所有的静态成员变量进行默认初始化 默认初始化完成后 对所有静态成员变量进行显示初始化
完成后 jvm会自动执行静态代码块 (静态代码块在栈中执行)(当有多个静态代码块时 执行顺序为你书写代码的先后顺序)所有代码块执行完毕 也就证明类加载完成
2.对象的创建过程:
3.当使用java命令运行java程序时 此时jvm启动 并去方法区下寻找有没有所创建对象的类存在 有则创建对象 没有就将这个类加载到方法区
在创建类的对象时 首先会去堆内存中开辟一块空间 开辟后分配空间(指定地址) 空间分配完后 会加载该对象中所有的非静态成员变量进行默认初始化 默认初始化后 调用相应构造方法到栈内存中 在栈中执行构造函数时,先执行隐式,再执行构造方法中书写的代码构造方法中的隐式:
第一步:执行super()语句 调用父类的没有参数的构造方法
第二步:对所有的非静态成员变量进行显式初始化(在定义成员变量时后面有赋值)
第三步:显式初始化完成之后,执行构造代码块
Ps.第二步第三步按照书写顺序执行
最后执行构造方法中书写的代码
当整个构造方法全部执行完,此对象创建完成,并把堆内存中分配的空间地址赋给对象名(此时对象名就指向了该空间)

什么是jvm

Jvm是一种用于计算机设备的规范,他是一个虚构出来的计算机 Java语言的一个非常重要的特点就是与平台的无关性,而使用java虚拟机是实现这一特点的关键 一般的语言 在不同平台上进行运行 需要编译成不同的目标代码 但是Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行 只需生成在java虚拟机上运行的目标代码,就可以在多重平台上不加修改的运行 这就是“一出编译 多出运行”的原因
在执行Thread.start()方法后,线程是不是马上运行:
当执行start()方法后 线程状态编程runnable(运行)状态 但是线程这个时候并不会马上运行 而是调用native方法 由他在操作系统中新建一个线程 但是操作系统上新创建的线程也是不会立即执行的 他会线程需要被cpu调度,分配了时间片之后才会真正的运行。因此jvm中的RUNNABLE状态其实对应了两个状态,ready和runnable。创建的新线程是ready状态,被cpu调度后成为runnale状态,这时候才是真正的运行状态。

Jvm堆在jdk1.7和1.8区别:

1.8版本用元数据区取代了1.7版本及以前的永久代。元数据区和永久代本质上都是方法区的实现。
不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存(也就是说jvm可以使用外边的内存)。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小
-XX:MetaspaceSize=N:设置metaspace区域的最大值 如果这个参数没有设置,那么就是通过mxbean拿到的最大值是-1,表示无穷大
-XX:MaxMetaspaceSize=N:表示metaspace首次使用不够而触发FGC的阈值,只对触发起作用,原因是:垃圾搜集器内部是根据变量_capacity_until_GC来判断metaspace区域是否达到阈值的

Gc收集相关算法:

(1)引用计数法 Reference Counting
给对象添加一个引用计数器,每过一个引用计数器值就+1,少一个引用就-1。当它的引用变为0时,该对象就不能再被使用。它的实现简单,但是不能解决互相循环引用的问题。
(2)根搜索算法 GC Roots Tracing
以一系列叫“GC Roots”的对象为起点开始向下搜索,走过的路径称为引用链(Reference Chain),当一个对象没有和任何引用链相连时,证明此对象是不可用的,用图论的说法是不可达的。那么它就会被判定为是可回收的对象。
JAVA里可作为GC Roots的对象
虚拟机栈(栈帧中的本地变量表)中引用的对象
方法区中的类静态属性引用的对象
方法区中的常量引用的对象
本地方法栈中JNI(即Native方法)的引用的对象
(3)标记-清除算法 Mark-Sweep
这是一个非常基本的GC算法,它是现代GC算法的思想基础,分为标记和清除两个阶段:先把所有所有需要回收的对象标记出来,然后把没有被标记的对象统一清除掉。但是它有两个问题,一是效率问题,两个过程的效率都不高。二是空间问题,清除之后会产生大量不连续的内存。

(4)复制算法 Copying
复制算法是将原有的内存空间分成两块,每次只使用其中的一块。在GC时,将正在使用的内存块中的存活对象复制到未使用的那一块中,然后清除正在使用的内存块中的所有对象,并交换两块内存的角色,完成一次垃圾回收。它比标记-清除算法要高效,但不适用于存活对象较多的内存,因为复制的时候会有较多的时间消耗。它的致命缺点是会有一半的内存浪费。

(5)标记整理算法 Mark-Compact
标记整理算法适用于存活对象较多的场合,它的标记阶段和标记-清除算法中的一样。整理阶段是将所有存活的对象压缩到内存的一端,之后清理边界外所有的空间。它的效率也不高。

什么是死锁?

两个进程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是两个进程 都陷入了无限的等待中。如何预防死锁:

处理死锁的思路如下:
预防死锁:破坏四个必要条件中的一个或多个来预防死锁。
避免死锁:在资源动态分配的过程中,用某种方式防止系统进入不安全的状态。
检测死锁:运行时产生死锁,及时发现思索,将程序解脱出来。
解除死锁:发生死锁后,撤销进程,回收资源,分配给正在阻塞状态的进程。
预防死锁的办法:
破坏请求和保持条件:
1、一次性的申请所有资源。之后不在申请资源,如果不满足资源条件则得不到资源分配。
2、只获得初期资源运行,之后将运行完的资源释放,请求新的资源。
破坏不可抢占条件:当一个进程获得某种不可抢占资源,提出新的资源申请,若不能满足,则释放所有资源,以后需要,再次重新申请。
破坏循环等待条件:对资源进行排号,按照序号递增的顺序请求资源。若进程获得序号高的资源想要获取序号低的资源,就需要先释放序号高的资源。
扩展资料
形成死锁的四个必要条件:
(1)互斥条件:一个资源每次只能被一个进程使用。
(2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

乐观锁与悲观锁区别:

悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源只给一个线程使用 而其他线程阻塞 用完之后 才会把资源让给其他线程 所以传统关系型数据库用这种锁比较多 操作前先上锁)
实现:使用了select…for update的方式将数据锁住 通过开启排他锁的方式实现了悲观锁
如:

select status from t_goods where id=1 for update;

此时id为1的那条数据就被我们锁住了 其他事务必须等这次事务执行完毕才能执行 保证了数据不会被其他事务修改
如:
Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
应用场景:多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适
问题:
1.加锁的机制造成数据库效率变低 并增加死锁的几率
2.在只读事务中 不会产生冲突 没必要用锁 只会增加系统负载压力
3.降低了并行性,一个事务如果锁定了某行数 据,其他事务就必须等待该事务处理完才可以处理那行数
乐观锁:
总是假设最好的情况 每次别人拿数据认为别人都不会进行修改 不上锁 但是在更新数据前会判断别人有没有进行更新(在数据初始化时指定一个版本号,每次对数据的更新操作都对版本号执行+1操作。并判断当前版本号是不是该数据的最新的版本号进行实现)乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,都是提供的乐观锁
应用场景:适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量
问题:两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就可能会遇到不可预 期的结果
总结
乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机智的其实都是提供的乐观锁。 相反,如果经常发生冲突,上层应用会不断进行 retry,这样反而降低了性能,所以这种情况下用悲观锁比较合适

多线程:


无论集成谁(excute(runable接口中提交方法)) 都有一个问题 就是无法立刻获取任务执行结果 可通过excutor线程池提供一些callable、future等方法进行提交任务实现:

线程池优点:
线程池关闭相关方法:

Serializable接口:
一般情况下定义:
Serializable接口就是:
一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才能被序列化

什么是序列化与反序列化?

序列化是将对象状态转换为可保持或传输的流格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据
反序列化同理就是将可传输的流格式恢复为对象的过程
这两个过程结合起来,可以轻松地存储和传输数据。
把对象转换为字节序列的过程称为对象的序列化
把字节序列恢复为对象的过程称为对象的反序列化
为什么要定义serialversionUID变量:
不定义运行时也会自行创建这个变量,但是强烈建议用户自定义一个serialVersionUID,因为默认的serialVersinUID对于class的细节非常敏感,反序列化时可能会导致InvalidClassException这个异常。用来辅助对象的序列化和反序列化的
详细过程:当序列化的时候系统将serialVersionUID写入到序列化的文件中去,当反序列化的时候系统会先去检测文件中的serialVersionUID是否跟当前的文件的serialVersionUID是否一致,如果一直则反序列化成功,否则就说明当前类跟序列化后的类发生了变化,比如是成员变量的数量或者是类型发生了变化,那么在反序列化时就会发生crash,并且回报出错误InvalidClassException
***声明式事物(即以配置或注解的方式对事物进行声明):
spring给了一个约定(AOP开发也给了我们一个约定),如果使用的是声明式事务,那么当你的业务方法不发生异常(或者发生异常,但该异常也被配置信息允许提交事务)时,Spring就会让事务管理器提交事务,而发生异常(并且该异常不被你的配置信息所允许提交事务)时,则让事务管理器回滚事务

事务的理解

用一个简单例子说明:银行转帐业务,账户A要将自己账户上的1000元转到B账户下面,A账户余额首先要减去1000元,然后B账户要增加1000元。假如在中间网络出现了问题,A账户减去1000元已经结束,B因为网络中断而操作失败,那么整个业务失败,必须做出控制,要求A账户转帐业务撤销。这才能保证业务的正确性,完成这个操走就需要事务,将A账户资金减少和B账户资金增加放到同一个事务里,要么全部执行成功,要么全部撤销,这样就保证了数据的安全性。
事务具有四个特性,也是面试常考的四个特性ACID:
A(原子性Atomicity):原子性指的是事务是一个不可分割的,要么都执行要么都不执行。
C(一致性Consistency):事务必须使得数据库从一个一致性状态,到另外一个一致性状态。
I(隔离性Isolation):指的是一个事务的执行,不能被其他的事务所干扰。
D(持久性Durability):持久性指的是一个事务一旦提交了之后,对数据库的改变就是永久的。

创建一个事务

事务其实可以划分为两大类:隐式的事务和显示的事务
隐式的事务很简单,比如我们的insert、delete、update、select这些语句都是隐式的事务。
显示的事务指的是带有很明显的开始和结束的标记,下面就来创建一个显示的事务。
步骤一:禁用步骤提交功能
set autocommit = 0;
步骤二:开启一个事务
start transaction;
步骤三:sql语句
update table user set money=500 where name = “张三”;
update table user set money=1500 where name = “李四”;
步骤四:结束事务
commit(提交)或者是rollback(回滚)。如果确定我们的语句没有问题,那么我们就可以commit,如果认为我们的语句有问题,那就rollback。

在这里新建了一个表,然后插入了两条数据。下面我们使用事务,来更新一下:

在这里我们使用的是commit进行提交。当然如果突然发现我们之前的操作有错误,那就可以使用rollback。

事务的隔离级别

我们接下来就看看这四个隔离级别的具体情况
读未提交(Read Uncommitted)
读未提交,顾名思义,就是可以读到未提交的内容。
因此,在这种隔离级别下,查询是不会加锁的,也由于查询的不加锁,所以这种隔离级别的一致性是最差的,可能会产生“脏读”、“不可重复读”、“幻读”。
如无特殊情况,基本是不会使用这种隔离级别的。
读提交(Read Committed)
读提交,顾名思义,就是只能读到已经提交了的内容。
这是各种系统中最常用的一种隔离级别,也是SQL Server和Oracle的默认隔离级别。这种隔离级别能够有效的避免脏读,但除非在查询中显示的加锁,如:

select * from T where ID=2 lock in share mode;
select * from T where ID=2 for update;

不然,普通的查询是不会加锁的。
那为什么“读提交”同“读未提交”一样,都没有查询加锁,但是却能够避免脏读呢?
这就要说道另一个机制“快照(snapshot)”,而这种既能保证一致性又不加锁的读也被称为“快照读(Snapshot Read)”
假设没有“快照读”,那么当一个更新的事务没有提交时,另一个对更新数据进行查询的事务会因为无法查询而被阻塞,这种情况下,并发能力就相当的差。
而“快照读”就可以完成高并发的查询,不过,“读提交”只能避免“脏读”,并不能避免“不可重复读”和“幻读”。
可重复读(Repeated Read)
可重复读,顾名思义,就是专门针对“不可重复读”这种情况而制定的隔离级别,自然,它就可以有效的避免“不可重复读”。而它也是MySql的默认隔离级别。
在这个级别下,普通的查询同样是使用的“快照读”,但是,和“读提交”不同的是,当事务启动时,就不允许进行“修改操作(Update)”了,而“不可重复读”恰恰是因为两次读取之间进行了数据的修改,因此,“可重复读”能够有效的避免“不可重复读”,但却避免不了“幻读”,因为幻读是由于“插入或者删除操作(Insert or Delete)”而产生的。
这是数据库最高的隔离级别,这种级别下,事务“串行化顺序执行”,也就是一个一个排队执行。
这种级别下,“脏读”、“不可重复读”、“幻读”都可以被避免,但是执行效率奇差,性能开销也最大,所以基本没人会用。
上面的事务在单个情况下一般不会出现什么问题,但是如果同时运行多个,就会出现问题了。我们知道并发操作总是会出现各种各样的问题,对于事务来说就会出现下面三个典型的问题
(1)脏读
有俩事务T1,T2。如果T1读了一条数据,这条数据是T2更新的但是还没提交,突然T2觉得不合适进行事务回滚了,也就是不提交了。此时T1读的数据就是无效的数据。
(2)不可重复读
有俩事务T1,T2。如果T1读了一条数据,之后T2更新了这条数据,T1再次读取就发现值变了。
(3)幻读
有俩事务T1,T2。如果T1读了一条数据,之后T2插入了一些新的数据,T1再次读取就会多出现一些数据。
如何去解决这些问题呢?既然多个事务同时运行不好,那就把他们隔离开来。这时候就用到了事务的隔离性。

mysql默认的事务隔离级别为repeatable-read,Oracle默认的是read-committed,
为什么会出现“脏读”?因为没有“select”操作没有规矩。
为什么会出现“不可重复读”?因为“update”操作没有规矩。
为什么会出现“幻读”?因为“insert”和“delete”操作没有规矩。
“读未提(Read Uncommitted)”能预防啥?啥都预防不了。
“读提交(Read Committed)”能预防啥?使用“快照读(Snapshot Read)”,避免“脏读”,但是可能出现“不可重复读”和“幻读”。
“可重复读(Repeated Red)”能预防啥?使用“快照读(Snapshot Read)”,锁住被读取记录,避免出现“脏读”、“不可重复读”,但是可能出现“幻读”。
“串行化(Serializable)”能预防啥?排排坐,吃果果,有效避免“脏读”、“不可重复读”、“幻读”,不过效果谁用谁知道。

Java基础之十年面试杂记相关推荐

  1. java基础及架构师面试问题

    Java基础面试题整理 原创Java程序员-张凯 最后发布于2019-02-08 09:09:06 阅读数 39778 收藏 展开 面向对象的三个特征 封装,继承,多态.这个应该是人人皆知.有时候也会 ...

  2. Java基础知识(十) 多线程

    Java基础知识 1. 什么是多线程?它与进程有什么区别?为什么要使用多线程 2. 同步和异步有什么区别 3. 如何实现Java多线程 4. run()方法与start()方法有什么区别 5. 多线程 ...

  3. Java基础知识点总结(面试版)

    目录 基础篇 4 一.JDK常用的包 4 二. Get和Post的区别 4 三. Java多态的具体体现 4 四. StringBuffer StringBuilder String 区别 5 五. ...

  4. Java开发和测试开发面试杂记

    文章目录 重载和重写 多态 泛型 抽象类和接口,继承的区别 进程和线程的区别 @Resource注解 bean的生命周期 微信聊天测试 时间复杂度 int和Integer的区别 HTTP和HTTPS的 ...

  5. 扎实的java基础_如何做到面试官要求的“扎实的Java基础”

    那么,如何具有扎实的Java基础? 总结成一句话--玩好JDK. 总的来说,JDK包括三部分,第一:Java运行环境(JVM),第二:Java的基础类库(lang .uitl.io等等),第三:Jav ...

  6. Java基础知识记忆(面试准备)

    Java知识记忆 Java技术体系 JDK IDEA Java基础语法 Java基础语法 1.类型转换问题 自动类型转换 表达式的自动类型转换 强制类型转换 2.运算符 2.1 算术运算符(理解) 2 ...

  7. JAVA基础(三)-面试篇

    系列文章目录 JAVA基础系列只是列出博主在刷牛客JAVA转项练习的总结 JAVA基础篇一:继承.多态.导包问题 .排序.变量初始值等 JAVA基础篇二:父子类问题.Map空值问题.异常.真假问题等 ...

  8. JAVA基础(一)-面试篇

    系列文章目录 JAVA基础系列只是列出博主在刷牛客JAVA转项练习的总结 JAVA基础篇一:继承.多态.导包问题 .排序.变量初始值等 JAVA基础篇二:父子类问题.Map空值问题.异常.真假问题等 ...

  9. Java基础总结之(面试)

    文章目录 Java标识符 Java修饰符 访问权限修饰符 访问控制和继承 非访问权限修饰符 局部变量修饰符 接口 接口中方法修饰符 运算符 算术运算符 一元运算符 二元运算符 算术赋值运算符 赋值运算 ...

  10. 每日学习-Java基础(十)接口和继承10(内部类)

    一.内部类 内部类 // 内部类有四种类型 // 1-非静态内部类 // 2-静态内部类 // 3-匿名类 // 4-本地类 1.非静态内部类 package ia10_innerClass;publ ...

最新文章

  1. 活久见的设备连接故障
  2. javaScript 实现多选框全选/反选及批量删除
  3. oracle11 不更新记录,oracle11g 使用first_value获取表中不连接的ID号及掉失记录数量...
  4. C语言试题六之使字符串的前导*号不得多于n个;若多于n个,则删除多于的*号;若少于或等于n个,则什么也不做。字符串中间和尾部的*号不删除。
  5. 我的世界方块云服务器bug,我的世界:两个方块能无限刷经验?这装置太BUG了
  6. C语言32个关键字详解
  7. 怎么把原本一页的PDF拆分成单独的两页?
  8. 职场技巧:内向者如何修炼成社交达人
  9. JPopupButton
  10. bitbucket配置_如何配置Bitbucket的ssh
  11. MCUXpresso开发NXP RT1060(3)——移植LVGL到NXP RT1060
  12. 基于深度学习的多任务人脸属性分析(基于飞桨PaddlePaddle)
  13. Python爬虫js处理
  14. AI面试官上岗,求职者该如何应对?
  15. 最经典的子网划分方法【简单易懂】
  16. 应用程序“HMW035347”中的服务器错误
  17. linux编译GDAL
  18. INOBITEC PRO DICOM VIEWER 2.9.0 Crack
  19. 数学建模学习(63):2022美赛单变量深度学习LSTM 时间序列分析预测
  20. printf函数的重定向

热门文章

  1. ModelSim SE 6.5破解
  2. debian sources
  3. 与Android热更新方案Amigo的亲密接触
  4. Mongodb语法学习:查询
  5. Unite'17 Shanghai再一次问候
  6. [Visual Studio] VS2012调试时很慢的解决方案
  7. Zookeeper实现简单的分布式RPC框架
  8. 2.企业应用架构模式 --- 组织领域逻辑
  9. 青岛科技大学C语言程序设计,青岛科技大学c语言试题库
  10. spring-boot配置文件中server.context-path不起作用