1、线程池的原理
线程池的七大参数:核心线程池,最大线程池,空闲线程存活时间,时间单位,阻塞队列,线程工厂,拒绝策略。
我理解的线程池原理。任务进来先去拿核心线程池的线程。当核心线程池满了。任务进阻塞队列。当阻塞队列满了。线程池自动扩容到最大线程池。阻塞队列里的任务去拿线程。当最大线程池和阻塞队列多满了。就会使用拒绝策略。当任务量下来。最大线程池空闲后。空间线程超过空闲线程存活时间。线程池大小变为核心线程池大小。
阻塞队列我们是使用的 LinkedBlockingQueue()需要自定义大小。不然默认是intger.max大小
拒绝策略使用:AbortPolicy。默认抛出异常
2、nacos的原理
nacos是一个提供了服务发现、服务配置和服务管理的特性集。
nacos的服务注册原理:注册中心分为服务端(server)和客户端(client)。服务端为客户端提供注册发现服务和注册配置服务。服务注册的策略是客户端每5秒向服务端发送一次心跳,心跳带上了服务名,ip地址。服务端口等信息。同时服务端也会向客户端发起服务健康性检查。如果15秒内无心跳。则认为实例不健康。如果30秒来再次检查失败。则剔除实例。
nacos的配置中心原理控制台---->(修改配置)----->nacos----->检查到配置变化。然后推送给服务。
3、sentlener 限流的原理
在sentinel里面,所有的资源都对应一个资源名称(resourceName),每次调用会创建一个Entry对象,在Entry创建的同时也会创建出一系列的功能插槽。这些插槽就是核心功能。
这些槽里带着一下数据信息。然后再根据滑动窗口的算法来进行限流
滑动窗口大致就是。例如设置单位时间就是一个窗口。窗口可以分隔多个更小的时间单元
随着时间的推移。窗口会向右移动。比如一个接口一分钟限制调用1000次。把1分钟分割为10个单元格,每个单元格就是6秒。
1)NodeSelectorSlot:负责收集资源的路径,并将这些资源的调用路径。以树状结构存储起来。用于根据调用路径来限流降级
2)ClusterBuilderSlot :用户存储资源的统计信息已经调用者信息。例如资源的RT(运行时间,QPS,thread count) 等等。这些也可以用来做限流。降级
3)StatisticSlot :则用于记录、统计不同维度的runtime指标监控信息
4)FlowSlot :则用于根据设置的限流规则以及sort统计的状态。来进行流量控制
5)AuthoritySlot :根据配置的黑白名单和调用来源信息。做黑白名单控制
6)DegradeSlot:统计信息一级设置的规则。来做熔断降级
7)SystemSlot:通过系统的状态。来控制总的入口流量;
是用QPS 限流。根据他没设置的阈值来触发(没秒超过多少请求)。或者容错率 。调用这个方法出错多少次来触发。还可以根据高频热点词来限流。
4、currentHashMap的原理
锁的粒度就是HashEntry(首节点)
5、redis的原理
6、spring的原理
就是IOC和AOP
IOC就是控制反转也就是让我们不用去手动new 一个对象。而是使用ioc容给我创建好自己拿出来就行了。在bean里面配置类。通过applicationContext 获取ioc容来拿对象。
DI:依赖注入:就是容器在运行的时候那个组件需要另外一个类的时候,容器通过反射的形式将容器中准备好的对象注入那个组件中。
用注解快速加入到容器中。
1)给组件加上注解任何一个@Controller,@Server,@Repository,@Componet
2)告诉spring,自动扫描注解的组件,依赖context标签
<context:component-san base-packgae="包名"/>
根据ApplicationContext ioc=new ClassPathXmlApplicationContext("");
自动装配有4中 按类型(Bytype) 按名字(Byname)。按构造方法。还有一个默认的(default)后面两种其实也是安装类型自动装配的。都是使用setting注入
AOP的原理就是动态代理+反射
面向切面编程。就是把日志。打印或者参数校验抽取出来成为一个公共的模块话。
其中有关键字:
@Service+目标类
@Component+切面类+@Aspect
在目标方法之前+@Before
在目标方法之后+@After
在目标方法返回之后+@AfterReturning
在目标方法出现异常+@AfterThrowing
@Around:环绕
@Before("execution(复制目标方法 方法要加类名)去掉方法名变成*就是所有方法
在配置文件中配置扫描包以及基于注解AOP的配置<aop:aspectj-autoproxy>
oinPoint 获取目标的详细信息
1)横切关注点(抽取出来的非核心的代码)
2)切面 (类型)
3)通知 (要做的几件事。如日志初始和参数校验)
4)目标(方法)
5)代理(目标对象应用通知后创建的代理对象(抽取出来的代码整合的类))
6)连接点(在方法前,还是后。获取捕获异常后)
7)切入点(放在哪里执行 @Before)
配合ASpectJ注解声明切面
@Before 前置通知
@After 后置通知
@AfterRunning:返回通知
@AfterThrowing:异常通知
@Around:环绕通知
切面。就是抽象的那个类
切入点:就是打印日志的那个后面方法
7、springmvc的原理
请求到前端控制器(DispatchServlet)-->根据路径找到对应的路径处理器(HandlerMapping)----->然后执行目标方法--->执行适配器(HandlerAdpater)---返回模型和视图(ModelandView)---->视图解析器(ViewReslover)---->最后返回数据给页面响应
8、mybatis的原理
四部:
第一步获取SQLSessionFacotory工厂对象。(根据配置Mybatis配置文件,初始化Configuration对象)
第二步获取SQLSession对象
会创建一个DefaultSQLSession对象。里面包含Configruation对象以及Executor
第三步获取MapperProx的代理对象
第四步获取执行查询
第一步调用DefualtSQLSession的增删改查方法。
第二步创建stateMentHander处理器同时创建ParameterHander和 ResultHander()
第三步调用sateMentHander预编译参数和设置参数值
使用ParameterHander来给sql设置参数值
第四步 调用stateMentHander的增删改查方法
第五步ResultHander封装结果
9、主从复制。读写分离的原理
10、jvm的原理
11、srpingboot的原理
@SpringBootApplication注解里面有三大注解
@ComponentScan 自动扫描并加载符合条件的组件
@EnableAutoConfiguration 自动配置(有符合自动配置条件的bean定义加载到IoC容器,其实里面是@Import注解自动导包)
@SpringBootConfiguration 装配所有bean事务,提供了一个spring的上下文环境。
springboot的原理主要两部分
保存主配置类
判断当前是否一个web应用
会初始化MATE-INFO/spring.factories里的ApplicationContextinitialter容器
会初始化MATE-INFO/sprng.factories里的AppliacationContextListener容器监听器
会加重主配置类的信息
run()方法启动的时候
1、获取getRunListener();获取运行的监听器
2、lister.start() ;运行监听器的start()方法
3、parperEnvironment();准备方法
4、调用打印printBanner();spring图片的方法
5、cerateApplicationContext();创建容器
6、prepareContext()将准备好的环境加入的容器中核心方法。或去所有的配置以及其他的ioc容器配置加载到已经准备好的容器中
7、然后refreshContext(context)刷新容器
8、从容器中获取回调方法的finished()方法
9、然后返回启动的容器。
12、es的原理
13、悲观锁和乐关锁
悲观锁:悲观的认为当你去拿这个数据的时候。别人会修改这个数据。所以加锁
如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁
行锁:innoDB支持行锁和表锁,会出现死锁。发送锁冲突几率低。并发高。
表锁:不会出现死锁。发送锁的冲突几率高,并发低
行锁:共享锁(读锁):当一个事务对某几行上读锁时。允许其他事务对这几行进行读操作。但是不允许进行写操作。也不允许其他事务给转给几行上排他锁。
排他锁(写锁):写锁,当一个事务对某几行写锁是。不允许其他事务写。单允许读
死锁:就是两个事务相互请求别人没释放的数据,
Java 中的 Synchronized
乐观锁:乐观的任务别人去拿你这个数据的时候。别人不会去修改这个数据。但是在更新的时候会判断一下在此期间别人有没有修改这个值。版本号和CAS 比较并交换
14、单列模式的饿汉式和懒汉式
最体现的就是。ArrayList的add方法。在JDK1.7以前add方法默认底层数组给你是10的长度。
但是JDK1.8之后第一次是一个null的。当你去添加数据的时候去判断一下是不是第一次添加数据。如果是再给你数组长度默认为10的长度
15、StringBuffer与StringBuilder的区别以及String
StringBuffer是线程安全的。StringBuilder(1.5出的)是线程不安全的 效率高·。如果不存在线程安全问题使用StringBuilder
String:不可变的字符序列 ;底层使用char[] 存储
StringBuffer:可变的字符序列;底层使用char[] 存储 方法都加了synics锁
StringBuilder:可变的字符序列;底层使用char[] 存储
new String(); // char[] value=new char[0];
new String("abc"); char[] value=new char[]{'a','b','c'};
new StringBuffer();构造方法是 调用了char[] value=new char[16]
new StringBuffer("abc");//构造方法是char[] value=new char["abc".length()+16];
扩容问题:如果添加的数据底层数组盛不下了,那就需要扩容底层的数组。
默认情况下,扩容为原来容量的2倍+2,同时将原有数组中的元素复制到新的数组中。
常用StringBuffer
16、HashMap的原理
17、List接口常用的实现类
ArrayList(JDK1.2): 线程不安全的。效率高。底层使用Object[] 存储
LinkedList(JDK1.2):对于频繁的插入、删除操作、使用此类效率比ArrayList高,底层使用双向链表存储
Vector(JDK1.0):线程安全的。效率不高,底层使用Object[] 存储
效率问题很简单:ArrayList如果存储1000个数据,要往中间插入一个。后续元素的一个一个移动。如果删除一个的一个一个往前移。这样的操作会非常耗系统性能的。
链表就是每个元素有两个指针。删除元素的后指针不止像原先的。执行后一个。删除元素的前指针不再执行自己。执行前一个元素。
三个实现类的相同点就是实现了list的接口,存储数据的特点相同:存储有序的,可重复的数据
底层都是使用的Object[]存储
ArrayList的源码分析;JDK 7情况下
ArrayList list=new ArrayList();//底层创建了长度是10的Object数组elementData
当你添加的元素超过底层数组的长度。则会自定扩容。默认情况下。扩容为原来的容量的1.5倍。同时需要将原有数组中的数据复制到新的数组中。
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
由此可以知道。在开发中我们需要自定好数组的长度。避免资源浪费。用ArrayList(int i)带参数的构造方法。默认容量为你传入的数组的的长度。
JDK 1.8 ArrayList的变化
ArrayList list=new ArrayList();//底层创建了初始值为0{}的Object数组elementData 。并没有创建长度。
list.add(123);第一次调用add()时。底层才创建了长度为10的数组。并将数据123添加到elementData中
后续就是后JDK1.7一样自动扩容
总结:
JDK7中ArrayList的对象的创建类似于单列模式的饿汉式,而JDK8中的ArrayList的对象的创建类似于单列的懒汉式,延迟了数组的创建,节省内存。
LinkedList的源码分析:
LinkedList list=new LinkedList();内部声明了Node类型的first和last属性。默认值为null.
list.add(123);//将123封装到Node中,创建了Node对象。
其中Node定义为:体现了LinkedList的双向链表的说法。
list:遍历用Iterator +while()
常用方法:iterator.remove
add();
add(1,"aa")//在index位置插入元素
get();
remove();
set();
18、set
HashSet:线程不安全的。可以存储null值
LinkedHashSet:做为HashSet的子类。遍历其内部数据时,可以安装添加的顺序遍历。对于频繁的遍历操作。LinkedHashSet效率高于HashSet
TreeSet:可以安装添加对象的指定属性。进行排序
Set:存储无序的。不可重复的数据。
要求:向Set中添加的数据。其所在的类一定要重写hashCode()和equals()方法
如果用了@Data注解可以不写。里面默认添加了这两个方法
1)、无序性:不等于随机性。存储的数据在底层数组中并不是按照数组所以的顺序添加。而是根据数据的哈希值。
2)、不可重复性。保证添加的元素按照equals()判断时。不能返回true.即:相同的元素只能添加一个。
添加元素的过程(HashSet):
我们想HashSet添加元素a,首先调用元素a所在类的hashCode方法。计算元素a的哈希值。
次哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置。判断数组次位置上是否已经有元素。
如果此位置上没有其他元素,则元素a添加成功。
如果此位置上有其他元素b(或者以链表形式存在的多个元素),则比较元素a与元素b的hash值:
如果hash值不相同。进而需要调用元素a所在类的equals()方法;
如果hash值相同。进而需要调用元素a所在类的equalas()方法;
equals()返回true.元素a添加失败
equals()返回false,则元素a添加成功。。
对于添加成功的情况2和情况3而言:元素a已经存在指定索引位置上数据以链表的方式存储
JdK8:一直往下添加元素
HashSet底层就是 :数组+链表
TreeSet:
1)向TreeSet中添加的数据,要求是相同类的对象。
2)两种排序方式:自然排序(实现Compareable接口)和定制排序(Comparator)
3)自然排序中。比较两个对象是否相同的标准为:compareTo()返回0.不再是equals().
19、redis常用的几种数据类
String 字符串类型,(常用命令,get k 或者值 set k v 设置值 删除del k)
hash 键值对类型。 ( hset user k v, hget user id ,hdel user name)
list字符串列表类型。(lpush 想左变添加元素 rpush向右边添加元素 lpop 从左边出一个 rpop从右边出一个 )
set 无序的string类型的集合。(zadd zset01 k v,zrange zset01 0 -1,zrem zset01 v5)
sorted set 有序五重复的string类型的集合
20、redis的持久化
redis的持久化有两种
rdb这种是通过在指定时间范围内将内存中的快照写入磁盘。恢复时讲文件直接读取到内存里面
redis会是用另一个子线程将数据持久化。会先将数据存在临时文件中,当数据持久化后。替换上次持久化的文件
快照就是redis会复制当前线程的另一个线程。新线程里面的数据和原先的一致。并且做为原来线程的子线程
-----使用
在redis的配置文件中 可使用save XXX(多少次操作) XXX(分钟) 命令
不使用 save "";就可以停止
恢复的就是将备份的rdb文件方正redis的安装目录下就可以了。
------特点
缺点是数据一致性较差。会存在一定的数据丢失。就是最后一次持久化的操作如果宕机会丢失最后一次持久化操作
Aof是以日志的形式记录每条操作的命令。该文件只能追加不能修改。redis重启的时候就会每次重新执行一遍这个命令。
文件是aof文件,在redis.conf 里面appenddonly 命令设置 默认是no 改成yes就是开启
然后将aof的文件放在在安装目录下。
如果有写坏的aof文件可以使用 redis--check-aof --fix进行修复
当aof文件越来越大。超过系统默认的设定值64m 他就会进行重写。压缩aof文件
重写原理就是:当aof文件过大是,会单独开辟出来一个线程来进行重写
他就是会遍历内存里的每个数据。然后在没条记录上加上 set 语句。重写aof文件时。并没有读取原先的aof文件。而是将内存里面的数据没一个都读取了一遍。形成一个新的aof文件。
配置设置 auto-aof-rewrite-percentage XX
auto-aof-rewrite-min-szie XX
设置同步机制:
默认 没秒appendfsync everysec
含有其他的每次更新 执行 always
不同步:no
特点:数据性一致性高。最多丢失也就是一秒是的数据
最终总结:
如果数据性一直不高。运行一定时间备份的。可以使用rdb 效率高
如果数据性高。可以使用aof 效率低
如果不考虑性能 。可以同时开启两个。默认先加载aof
如果只做数据缓存。可以不使用这两个模式。
21、垃圾回收器
有四大中
串行垃圾回收器(serial 复制):单核 :单线程回收。效率高。会停止用户使用的线程 复制算法
并行垃圾回收器(per new 复制算法):多核:多线程回收器。效率还可以。会停止用户使用的线程。
并发垃圾回收器(GMS 标记清除):多线程回收器。会减少用户停止的相应时间。
G1垃圾回收器:将堆内存分割成不同区域然后并发垃圾回收
22、es的倒序索引的原理
倒排索引的原理就是:把输入的词给拆分。通过分词器进行特定的处理,然后根据处理的结果进行排序。es内置有
standard analyzer、
默认分词器
按词切分,支持多语言
小写处理
simple analyzer、
按照非字母切分
小写处理
whitespace analyzer、
空白字符作为分隔符
language analyzer。
提供了30+种常见语言的分词器
先通过倒叙索引查词对应的文档id. 然后通过id的正序索引找到对应的文档内容
倒叙索引一般是由
单词词典:它的实现一般是B+数据
倒叙列表实现
- 倒排列表记录了单词对应的文档集合,由倒排索引项(Posting)组成
23、feign的原理
spring cloud 的 openfeign
openfeign是spring cloud在feign的基础上支持了spring mvc的注解。
比如他可以解析@ requestMapping注解下接口。并通过动态代理的方式生产实现类。
实现类中做负载均衡并调用其他服务
他的使用方法也简单。在启动类添加@EnableFeignClients,在远程服务接口上添@FeignClient();
feign的是一个声明式的 web 服务器端,编写webService客户端更简单。基于http协议
并且他内置了Ribbon,用来做客户端负载均衡。也能去调用服务注册中心的服务。
Ribbon 是基于Netfilx Ribbon实现的一套客户端负载均衡的工具+restTemplate
负载均衡的算法有 轮询,随机。
他的算法其实就是rest接口的请求次数和服务器集群总数量求余=实际调用服务器位置下标
服务集群的下标都在一个list数组里面。根据下标就可以取得对应的服务器地址。就可以请求。
根据这个算法,我们自己可以定义负载均衡的方式,自己根据自定义的算法实现LoadBalancer接口
24、Map --双列数据,存储key-value对的数据。
HashMap:Map的注意实现类,线程不安全,效率高;可以存储null的key和value
LinkedHashMap:保证在遍历map元素是,可以按照添加的顺序实现遍历
原因:在原有的HashMap底层结构基础上。添加了一对指针,指向前一个和后一个元素
根据这个指针指向就可实现。
对于频繁的遍历操作。此类执行效率高于HashMap
treeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序和定制排序
底层使用红黑数
HashTable:做为古老的实现类,线程安全的,效率低;不能存储null的key和value
Properties:常用来处理配置文件。key和value都是String类型
HashMap的底层:数组+链表(JDK7及以前)
数组+链表+红黑数(JDK8)
一、
1)、HashMap的底层实现原理
2)、HashMap和Hashtable的异同
线程不安全,效率高;可以存储null的key和value
线程安全的,效率低;不能存储null的key和value
3)、CurrentHashMap与Hashtable的异同?
二、Map的结构:
Map中的key :无序的,不可重复的(使用Set存储所有的key)。 --->key所在的类要重写equals()和hashCode()方法
Map中的Value:无序的,可重复的 (使用Collection存储所有的value)---->value所在的类要重写equals()
一个键值对:key-value构成了一个Entry对象。
Map中的entry:无序。不可重复的。(使用Set存储所有的entry)
三、HashMap的底层实现原理?以JDK1.7为例说明:
HashMap map=new HashMap();
在实例化以后,底层创建了长度是16的一维数组entry[] ;
经过多次put添加后,
map.put(key1,value1);
首先。调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法后,得到在Entry数组中的存放位置。
如果此位置为上数据为null,此时的key-value1值添加成功
如果此位置上数据不为空,此时需要比较key1和已经存在的一个或多个数据的哈希值:
如果key1的哈希值与已经存在的数据的Hash值不同,此时key-value1添加成功。
如果key1的哈希值与已经存在的数据的Hash值相同。继续比较,key1所在类的equals(key2)
如果equals()返回false:此时key1-value1 添加成功
如果equals()返回true;此时value1替换value2
默认的扩容方式:当超出临界值16*0.75=12(且要存放的位置非空)是扩容为原来的2倍。并将原有的数据复制过来
JDK8 比较JDK7 在底层实现方面的不同
1、new HashMap():底层没有创建一个查长度为16的数组
2、jdk 8 底层的数组是:Node[],而非是Entry[]
3、首次调用put方法()时,底层创建长度为16的数组
4、jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树
当数组的某一个索引位置以链表形式存在的数据个数>8且当前数组的长度>64时
此时此索引位置上的所有数据改为使用红黑树存储。红黑树遍历查找效率高
HasMap的默认容量:16
HashMap的加载因子:0.75
扩容的临界值=容量*填充因子 16*0.75=12
链表中的长度大于该默认值8 且当前数组的长度>64。转换为红黑树
桶中的Node被树化的最小hash表容量64
四、LinkedHashMap的底层实现原理
和HashMap一致,只不过他重写了创建node的方法。里面多了befor,after//能够记录添加元素的先后顺序
总结:常用方法
添加:put(k,v)
删除:remove(k,v)
修改:put(k,v)
查询:get(k)
长度:size();
遍历:keySet()/values()/entrySet()
面试题:
HashMap中put/get方法的认识。HashMap的扩容机制?默认大小是多少?什么是负载因子(或填充比)?什么是吞吐临界值(或阙值)
五、Collections的方法常用
sort()升序排序
max()返回集合里面最大的数据
copy(x,y)将ylist集合的数据复制到x
面试题:Collection和Collections的区别
25、反射
反射被视为动态语言
动态语言就是在运行时代码可以根据某些条件改变自身结构
反射机制允许程序在执行期间借助反射APi取的任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后。在堆内存的方法区中就产生了一个Class类型的对象。这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。
1)、通过直接new的方式或反射的方式都可以调用公共的结构。开发中用那个
建议直接用new
2)、反射机制与面向对象中的封装性是不是矛盾的。如何待这两个技术
(封装性。例如单列模式。提供的公有方法获取比获取私有的方法获取实例更好。)
3)、类的加载过程
类文件(.java)---javac----字节码文件(.class)---->类加载器-----Class--->到方法区成为运行时数据结构
4)、获取Class实例的方式
三种。获取的都是同一个Class
1) 类.class
2)new 对象 .getClass
3) Class.forName(类的全路径名)
Class<Per> cla=Per.class;
System.out.println(cla);
//获取方式二:
Per p=new Per();
Class pClass = p.getClass();
System.out.println(pClass);
// 方式三:
Class aClass = Class.forName("com.cb.demo.反射.Per");
System.out.println(aClass);
方式四()通过类的加载器ClassLoader
ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class aClass1 = classLoader.loadClass("com.cb.demo.反射.Per");
5)、通过反射创建对应的运行时类的对象
Class clazz= Per.class;
Per o = (Per) clazz.newInstance();
6)获取属性结构
Class clazz= Person.class; //获取属性结构 //getFields();获取当前运行时类及其父类中声明为public访问权限的属性 Field[] fields = clazz.getFields();
clazz.getDeclaredFields();//获取当前运行是类中声明的所有属性。(不包含父类)
7)获取运行时类的方法结构
class.getMethods();//获取当前运行是类及其父类中声明的为public权限的方法
class.getDecalredMethods();获取当前运行时类中声明的所有方法
框架=注解+反射+设计模式
8)获取运行时类的构造器结构
clazz.getConstrutors()//获取当前运行是类中声明为public的构造器
clazz.getDecalredConstrutors()//获取当前运行时类中所有的构造器
9)如何操作运行时类中指定的属性
class.newInstance()//创建运行时类的对象
getDeclaredField("name")//获取运行时类中指定的变量名的属性
.setAccessible //保证当前属性是可访问的
.set(p,"tom")//获取。设置指定对象的此属性值
10)如何操作运行时类中的指定的方法
class.newInstance()//创建运行时类的对象
class.getDeclareMethod("show",String.class);//获取指定的某个方法
参数1:指定获取方法的名称。参数2:指定获取的方法的形参列表
.setAccessible(true) //保证当前属性是可访问的
.invoke(p,"CNN")//参数1:方法的调用者。参数2:给方法形参赋值的实参
invoke()的返回值即为对应类中调用的方法的返回值。
面试题:
1)什么是反射:
反射是运行是在运行期。对于任何一个类。都能够知道他的方法,属性值。对于任何一个对象都能调用他的方法和属性值。这种动态的获取信息和动态的调用对象方法称为反射
2)
26、srping的事务
事务的特性有四种
ACID 原子行。一致性。持久性。独立性
隔离级别有四种:
读未提交
读已提交
可重复
串行化。
出现的现象有
脏读
不可重复读
幻读
spring的事务他有两种。
一种是编程式事务:编程式事务是用元素的JDBC api实现的。编程式事务需要将代码注入核心代码里面。这样式会造成代码臃肿。
一种是声明式事务。声明式事务他将代码从业务代码里面剥离出来,以声明的方式来实现管理,
声明式事务可以同借助Spring aop框架实现声明式事务管理。
事务的传播行为:当事务的方法被另为一个事务方法调用时。必须指定事务后续怎么做。是开启新事务,还是在本事务运行。
这样有7中行为:
required 如果有事务就在运行,当前方法在事务内运行。如果没有就启动一个新的,在新的事务内运行
required_new:当前的方法必须启动新事务。并且在自己的是事务内运行。如果有事务再运行,就要讲他挂起
supports:如果有事务在运行。当前的方法就在这个事务内运行。否则他可以不运行在事务中
not_supporte:当前的方法不应该运行在事务中,如果有运行的事务,将他挂起
mandatory:当前的方法必须运行在事务内部。如果没有正在运行的事务,就抛出异常
never:当前的方法不应运行在事务中,如果有运行的事务,就抛出异常
nested:如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则就启动一个新的事务。并在他自己的事务内运行。
总结:抛出异常有两种。挂起事务有两种。启动一个新的事务有两种。不运行在事务中一种
27、分布式事务
为什么有分布式事务。因为分布式系统经常宕机,网络异常。导致消息丢失等等。
CAP理论。
C 一致性
A:可用性
P:分区容错性
CAP定理就这三个要素最多只能同时实现两点。不能三者兼顾。
二P 总是成立。 所以只剩下AP和CP 经过大系统的考虑。能容忍数据不一致性。
不能容忍系统不可用性。
所以分布式系统大部分是定义为CP
BASE理论又是对CAP理论的延续。即做不到强一致性,做到弱一致性。即当时可以出错。但是后面最终保证一致。
而我们采用的是柔性事务即遵循BASE理论+最大努力通知型方案 -支付的时候
即业务代码加上+MQ消息中间件。设置最大通知次数。达到通知次数后即不再通知。
柔性事务+可靠消息+最终一直性方案。---订单
try{ 记录到数据库持久化 }catch{重复发}{} rabbitmq+后台离线处理。
28、redis的集群
配置主重模式配从。在从库的配置文件里配置slaveof 主机ip和端口
第一次的时候,复制原理是从机会向主机发一个异步命令,后台主机接受到的所有修改数据集的命令。发送给从机。这样就完成了首次启动同步
第二次的时间就是增票同步。主机将所有修改命令依次同步传递给从机完成同步
哨兵模式:就是监控主机服务。谁宕机了。就根据选举得票将从库变为主库
在配置文件下建sentinel.conf文件
sentinel monitor 名字 ip 端口 1 表示主机挂掉 去投票
29、mysql数据库索引
普通索引
唯一索引
主键索引
组合索引
全文索引
数据结构是B+树
树节点存的是索引序号,非树节点存的是数据。 mysql 会为索引序号维护一张表。便于查找
30、锁
sync 和lock异同
同:二者都可以解决线程安全问题
不同:sync机制在执行完相应的同步代码以后,自动的释放同步监视器
lock需要手动的启动同步同时结束同步也需要手动的实现
sync有代码块锁和方法锁。lock只有代码块锁
如果解决线程安全问题?有几种方式
用锁。或者lock代码块。或者sync的代码块。
sync同步代码块就是通过monitrorenterh和monitorexit指令。
其中monitroenter 指令指向同步代码块开始的位置。monitorexit指令指明同步代码块结束的结束的位置。当指向monitorenter指令时。线程视图获取锁也就是获取monitor的持有权。其中有一个计数器。获取锁和计数器为1,在执行monitorexit指令后计数器设为0就说明锁被释放了。
sync修饰方法没有指令 是通过一个表示 。这个标识指明了该方法是一个同步方法。通过访问标识来辨别一个方法是否同步方法。从而执行相应的的同步
cas 全称为比较和交换 是一个乐观锁。cas在不使用锁的情况下实现多线程之间的变量同步。
cas 算法设计到三个参数:需要读写的内存值;进行比较的值A;要写入的值。
只有当V的值等于A时,才会使用原子方式用新值B来更新V的值,否则会继续重试直到成功更新值。
31、mysql主从复制
1、主机将改变的记录到二进制日志中。这些记录过程叫做二进制日志事件
2、从机将主机的二进制日志拷贝到自己的日志中
3、主机从做续日志的事件。将改变的应用到数据库中。mysql复制是异步的
32、什么是线程安全的。当多个线程访问某个资源。不需要额外的同步。和线程直接的交替执行。获取到正确的结果。这样的获取就是线程安全的
33、getWay做了什么
网关做了转发、
- id: member_route
uri: lb://gulimall-member
predicates:
- Path=/api/member/**
filters:
- RewritePath=/api/(?<segment>/?.*),/$\{segment}
权限校验、
限流控制:
spring-cloud-alibaba-sentinel-gateway
GatewayCallbackManager.setBlockHandler
核心概念是:路由、断言、过滤
工作流程,请求进getway HandlerMapping 判断请求是否满足那个路由。满足就发给网关的WebHandler. 将请求交给过滤器连。在目标方法之前执行滤器连的pre方法。在目标方法执行之后再执行过滤器链的post方法。
34、nacos宕机后,还能服务调用吗。
还可以,原先调用的接口还可以。因为他有本地缓存。
1)为什么要将服务注册到Nacos?(为了更好的查找这些服务)
2)在Nacos中服务提供者是如何向Nacos注册中心(Registry)续约的?(5秒心跳包)
3)对于Nacos来讲它是如何判断服务实例的状态?(检测心跳包 ,15,30)
4)服务消费方是如何调用服务提供方的服务的?(RestTemplate)
35、过滤器和拦截器的区别
过滤器和拦截器的的执行顺序
过滤器------>拦截器---------目标方法------->拦截器----------->过滤器
36、tomcat的线程池和jdk的线程池有什么区别
tomcat的线程池当核心线程池满了是不会放入到阻塞队列里面的。tomcat是为了更快的响应
jdk的线程池是当核心线程池满了是会放入阻塞队列里面
37、HTTP协议请求过程
大致的流程:输入地址 –> DNS域名解析 –> 发起TCP的三次握手 –> 建立TCP连接后发起http请求 –> 服务器响应http请求,浏览器得到html代码 –> 浏览器解析html代码,并请求html代码中的资源(如javascript、css、图片等) –> 浏览器对页面进行渲染呈现给用户。
38、抽象类(abstract)
1)如果一个类使用抽象关键字abstract,那么这个类一定是抽象类
2)抽象类可以没有抽象方法
3)一个类如果包含抽象方法,那么这个类是不能被实例化的
abstract 抽象类
此类不能实例化
抽象类中一定有构造器,便于子类实例停调用。()
开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
抽象类不一定有抽象方法,但是抽象方法的类一定是抽象类。主要是为了保护抽象方法
子类基础抽象类。必须的重写所有的抽象方法。才能够被实例化。
没有重写父类的方法。那么次子类也是抽象类
abstract 不能用来修饰私有方法,静态方法
39、final
final 修饰方法时。不能被重写。就是最终版本的
final 修饰变量是,只能赋值一次。修饰参数时赋值过后。不能被赋值
final 修饰类时,不能被继承。(不需要在此类中继续修改)
40、static
1、用来修饰成员变量,将其变为类的成员。从而实现所有对象对于该成员的共享。独属一份
2、用来修饰成员的方法,将其变为类方法。
static 修饰的可以直接类.变量或者方法
static 在静态方法内,不能使用this关键字,super关键字
哪里使用属性:如果属性可以被多个对象锁共享的。不会随着对象不同而改变的
哪里使用方法:操作静态属性的方法。工具类上
总结:static final 用来修饰属性:全局常量
41、http网络传输的5层
1、应用层
2、运输层
3、网络层
4、链路层
5、物理层
42、servlet的生命周期
1、init()初始化
2、service() 方法来处理客户端的请求
3、销毁前调用destroy()方法
4、最后由JVM的垃圾回收器进行垃圾回收
43、io与nio的区别
io是基于阻塞式的流File
Nio是缓存式的流Buffer。后面的NIo2对功能进行了强化。
44、网络编程
就是直接或间接地通过网络协议与其他计算机实现数据交换。
通信双方:IP 端口号
网络通信协议:
通信协议的分层:
OSI分七层:
应用层,表示层,会话层:http,FTP,DNS
传输层:TCP,UDP
网络层:IP,ICMP,ARP
数据链路层,物理层:Link
TCP/IP分4层:
应用层:http,FTP,DNS
传输层:TCP,UDP
网络层:IP,ICMP,ARP
物理+数据链路层:Link
2)网络编程的两个要素
1.对应的问题:IP和端口号
在java中使用InetAddress类代码IP
获取本机getLocalHost()ip;
getHostName();/getHostAddress;
ip分类为:IPV4和IPV6;万维网和局域网
HTTP:80,FTP:21.Telnet:23
端口号与IP地址的组合的出一个网络套接字:Socket
2、都提供网络通信协议
传输层协议:
传输控制协议TCP
使用tpc前是需要先建立tcp连接,即三次握手。建立连接后就可以进行大数据量的传输。传输完毕就释放连接(例如打电话)
使用TCP前,须建立TCP连接,形成传输数据通过
传输前,采用三次握手。方式,点兑点通信。是可靠的
(一次的话。可能服务端没接受到。二次握手。可能客户端没接受到确认信息。所以第三次握手可以确认双方建立连接)
TCP协议进行通信的两个应用进程,客户端,服务端
在连接中可进行大数据量的传输
传输完毕,需释放已建立的连接(TCP四次挥手),效率低
TCP四次挥手:一般是客服端发起,我需要关闭连接,服务端接受到。然后发送信息给客户端说我要准备关闭连接了。然后服务端关闭连接。客户端接受服务端关闭的连接,然后发送信息看服务端是否断开。没信息就是断开了。
编程:socket对象
获取流fileInputStream io模式 。即阻塞模式获取
fis.read(读入)
os.write(写出)
UDP协议:
数将数据源封装成数据表。不需要建立连接。每个数据大小在64k内。因为不建立连接。所以不确认对方是否收到。所以这个传输方式不不可靠的。无需释放资源。所以开销小。速度快。
例如(网络视频)
将数据,源,目的封装成数据包,不需要建立连接
每个数据的大小限制周期64K内
发送不管对方是否准备好,接收发到也不确认,故是不可靠的
可以广播发送
发送数据结束时无需释放资源,开销小。速度快。
用户数据协议:UDP
UDP数据通过数据报套连接子DatagramSocket发送和接收,系统不保证UDP数据报一定能够安全送到目的地。也不能确定什么时候可以抵达。
DatagramPacket对象封装了UDP数据报,在数据报中包含了发送端IP地址和端口号以及接收端的IP地址和端口号。
DatagramSocket socket=new DatagramSocket();
String str="我是UDP";
byte[] data = str.getBytes();
InetAddress localHost = InetAddress.getLocalHost();
DatagramPacket packet=new DatagramPacket(data,0,data.length,localHost,9090);
socket.send(packet);
socket.close();
DatagramSocket socket=new DatagramSocket(9090);
byte[] buffer=new byte[100];
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
socket.receive(packet);
System.out.println(new String(packet.getData(),0,packet.getLength()));
socket.close();
URL网络编程
1、URL:统一资源定位符
2、格式:http://localhost:8888/examples?usename=TOm
协议 主机名 端口号 资源地址 参数列表
URL url=new URL("");
url.getProtocol //获取URL的协议名
url.getHost() //获取URL的主机名
url.getPort() //获取gaiURL的端口号
url.getPath()// 获取URL的文件路径
总结:TCP与UDP的区别:
TCP:可靠的数据;进行大数据量的传输。效率低
UDP:不可靠,以数据报形式发送。数据报限定为64K,效率高
45、Tomcat
修改Tomcat的端口。在配置文件server.xml配置文件中找到 Connecton 标签改port端口
46、Servlet
servlet是运行在服务器上的一个JAVA小程序,它可以接受客户端发送过来的请求,并响应数据给客户端。
servlet是一个接口。默认5个方法
1)、init()
2)、getServletConfig
3)、service() 专门用来处理请求和响应的
4)、getServletInfo()
5)、destory()方法
web.xml里面标签
<servlet>标签 给Tomcat配置Servlet程序
<servlet-mapping>标签给servlet程序配置访问地址
servlet的生命周期
1)、执行Servlet构造器方法
2)、执行init初始化方法
一和二在创建的时候就初始化了
3)、执行service方法
每次访问都会调用
4)、执行destroy销毁方法
47、HttpServlet 做请求分发
1)doget //在get请求的时候调用
2)doPost//在post请求的时候调用
48、HttpServletRequest类的作用
请求进入Tomcat服务器。Tomcat服务器会把请求过来的HTTP协议信息解析好封装到Request对象中,然后给到service方法(doget/dopost).我们可以通过HttpServletRequets对象,获取到所有请求的信息
(自定义servlet 实现HttpServlet 接口 也实现doget/dopost方法)使用doPost解决编码问题
49、HttpServletReponse类的作用
每次请求进来。Tomcat服务器都会创建一个Response对象传递给Servlet程序去使用。表示请求过来的信息。即所有响应的信息
设置doget方法解决返回响应数据页面乱码 设置服务器编码集和设置浏览器响应头
50、监听器(Listener)
监听器就是接口。
ServletContextListener 监听器
监听ServletContext 的创建和销毁 两个方法
51、过滤器(Filter)
也是接口
作用拦截请求,过滤响应。
3个方法:
1)init()
2)doFilter方法 //专门用于拦截请求。过滤响应 权限验证。有没有带令牌
3)destroy方法
过滤器的生命周期:
1)、构造方法
2)、init初始方法
一和二步在web工程启动的时候执行
3)、doFilte过滤方法
每次拦截请求。就会执行
4)、destory销毁方法
停止web工程的时候。就会销毁filter
52、过滤器与拦截器的区别
过滤器是基于函数回调的。拦截器是基于java反射机制的
过滤器依赖servlet容器。拦截器不依赖servlet容器
过滤器可以对所有请求作用,拦截器只能是实现了接口的类。
53、过滤器与监听器的区别
过滤器是拦截客户端的对应的请求。然后做对应代码的改变。每次请求对应上过滤器就可以执行
监听器是只做一个初始化的工资内容、设置一些基本的参数值。
总结关系流程图
请求---->过滤器----->拦截器------> 目标方法---->拦截器---->过滤器
监听器--->整个容器初始化。到销毁
54、mysql的聚集索引和非聚集索引区别
聚集索引:使用聚集索引的表。记录和索引保持一致。这样只要找到索引的值就能快速找到数据
非聚集索引:记录和索引的顺序不同。叶子结点下还有数据。需要再查询一遍
55、http总结:
什么是http协议
1) http协议是超文本传输协议的缩写。使用于从万维网服务器传输超文本到本地浏览器的传送协议
2)协议就是双方约定好的格式,确保双方都能理解这种格式
http协议的特点
1)http允许传输任意类型的数据。传输的类型由Content-Type加以标记
2)无状态。对于客户端每次发送的请求,服务器都认为是一个新的请求,上一次会话和下一次会话之间没有联系。
3)支持客户端/服务器模式
http长连接:
1)http长连接,指的是复用TCP连接。多个HTTP请求可以复用同一个TCP连接。这就节省了TCP连接建立和断开的消耗
2)HTTP1.0默认使用的是短连接。浏览器和服务器每进行一次HTTP操作就建立一次连接。任务结束就中断连接。
3)HTTP1.1起。默认使用长连接。要使用长连接,客户端和服务器的HTTP首部Connection都要设置keep-alive ,才能支持长连接
HTTP1.0 与HTTP2.0的区别
1)新的二进制格式:HTTP1.1基于文本格式传输数据;HTTP2.0采用二进制格式传输数据。解析更高效。
2)多路复用:在一个连接里,允许同时发送多个请求或响应。并且这些请求或响应能够并行的传输而不被阻塞。
3)头部压缩,HTTP1.1的headet带有大量信息,而且每次都要重复发送;HTTP2.0把header从数据中分离,并封装成头帧和数据帧,使用特点算法压缩头帧,有效减少头信息大小,并且HTTP2.0在客户端和服务器端记录了之前发送的键值对。对于相同的数据,不会重复发送。比如
请求A发送了所有的头信息字段,请求b则只需要发送差异数据,这样可以减少数据,降低开销。
4)服务端推送:HTTP2.0运行服务器想客户端推送资源。无需客户端发送请求到服务器获取
HTTP通信安全吗
1)HTTP是明文传输,容易被黑客窃听或篡改,不安全
2)使用HTTPS 来解决HTTP明文协议的缺陷。在HTTP的基础上加入SSL/TLS协议。依靠SSL证书来验证服务器的身份,为客户端和服务器端直接建立SSL通道。确保数据传输安全。
HTTPS的原理
先通过TCP的三次握手,然后客户端会发送一个HTTPS连接建立请求,客户端发送一个client包给服务。服务响应server包。再给客户单发送他的证书。这样双方经过秘钥交换。最后使用家伙的秘钥解密数据。
1)首先是TCP三次握手,然后客户端发送起一个HTTPS连接建立请求,客户端先发一个Client Hello 的包。然后服务器端响应Server hello 接着再给客户端发送他的证书。然后双方经过秘钥交换。最后使用交接的秘钥解密数据。
2)具体是协商加密算法,在 Client hello里面客户端会告知服务端自己当前的一些信息,包括客户端要使用的TLS版本,支持的加密算法。要访问的域名。给服务端生成的一个随机数等。需要提前告知服务器响应访问的域名以便服务器发送相应的域名的证书过来
3)服务端响应,告诉客户端,服务端选中的加密算法
4)服务端给客户发来了证书
5)客户端使用证书的认证机构CA公开发布的RSA公钥对该证书进行验证
6)验证通过之后。浏览器和服务器通过秘钥交换算法产生共享的对称秘钥。
7)开始传输数据。使用同一个对称秘钥来加解密
56、多线程
1)、什么是线程,是一个程序内部的一条执行路径,线程作为调度和执行的单位。每个线程拥有独立的运行栈和程序计数器(PC)
2)、什么是多线程
一个JAVA应用程序至少有三个线程:main()主线程。GC()垃圾回收线程。异常处理线程。
3)、多线程的优点
1)、提高应用程序的响应。对图形化页面更有意义。可增强用户体验
2)、提高计算机系统CPU的利用率
3)、改善程序结构。将即长有复杂的进程分为多个线程,独立运行。利于理解和修改。
4)、什么时候需要多线程
1)、程序需要同时执行两个或多个任务
2)、程序需要实现一些等待的任务时,
3)、需要一些后台运行的程序时。
5)、线程中常用的方法
1)、start();启动当前线程,调用当前线程的run()
2)、run();通常需要重新Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3)、currentThread():静态方法,返回执行当前代码的线程
4)、getName();获取当前线程的名字
5)、setName();设置当期线程的名字
6)、yield();释放当前cpu的执行权
7)、join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态。知道线程b完全执行完以后,线程a才结束阻塞状态。
8)sleep(); 线程睡眠
9)isAlive();判断当前线程是否存活
6)线程的调度,线程的优先级
三个提供的常量:10 --max 1--min 5--norm 默认优先级
方法:getPriority():返回线程优先值
setPriority(int ):改变线程的优先级。
设置优先级高。并不一定最先执行。
7)基础thread类和实现Runnable接口的方式
1)、优先选择Runnable接口的方式
2)、实现的方式更适合来处理过个线程共享数据的情况
相同点:都要重新run()。将线程要执行的逻辑声明在run()中。。都调用start()方法。
8)、线程的通信
1)、wait() 是的方法的线程进入阻塞状态。释放锁
2)notify() 一旦执行此方法,就会唤醒wait的一个线程。如果有多个线程被wait,就唤醒优先级高的。
3)notifyAll():一单执行此方法,就会唤醒所有被wait的线程。
以上三个方法必须使用在同步代码块或同步方法中
面试题:sleep()与wait()的异同
1、相同点:一旦执行方法,都可以是线程进入阻塞状态
2、不同点:sleep 不是释放锁。wait()释放锁
sleep是thread类的。wait()是Object类的
9)、线程的分裂
守护线程
用户线程
10)、线程的生命周期
1)、新建 new了一个线程
2)、就绪 调用了start()方法。进入线程队列等待CPU调度,
3)、运行 获得CPU资源时,进入运行 run方法
4)、阻塞 某种情况下被人为挂起。
5)、死亡 线程完成或者被强制性的终止或出现异常导致结束
11)、线程的同步
会出现线程安全问题,多个线程操作同一份共享的数据时。操作不当,会出现共享数据异常
解决:
sync:
1)同步代码块
sync(同步监视器){
}
同步监视器:俗称锁。任何一个类的对象。都可以充当锁。
要求:多个线程必须要共用同一把锁
2)同步方法
3)JDK1.5 Lock(锁)
1)实现ReentrantLoc( v)类获取锁
V:true 就是公平锁;false 非公平的。默认是false
结合
lock.loc();
try{
}finally{
lock.unlock();
}
12)死锁
57、nacos
1)为什么要将服务注册到Nacos(为了更好的查找这些服务)
2)为什么Nacos来讲它是如何判断服务实例的状态(检测心跳包。15.30)
3)在Nacos中服务提供者是如何向Nacos注册中心续约的(5秒新跳包)
4)服务消费放是如何调用服务提供方的服务的(RestTemplate)
服务负载均衡
1)@Bean注解的作用?就是自定义了一个组件交给spring容器管理
2)@Autowired注解的作用 ?告诉spring 这个对象依赖注入进来。使用
3)Nacos中的负载均衡底层是如何实现的?通过Ribbon实现。Ribbon提供了一些负载均衡的算法
4)Ribbon是什么?是Netfix公司提供的负载均衡客户端,一般应用于服务的消费方法
5)Ribbon可以解决什么问题?基于负载均衡实现服务调用。
6)Ribbon内置的负载均衡策略有哪些?通过实现IRule接口可以看出有
(随机,轮询,权重,重试,最低并发。可用过滤策略)
7)@LoadBalanced的作用是什么?通过RestTemplate对象,用户告诉Spring对象,在使用RestTemplate进行服务调用时,这个调用过程会被一个拦截器进行拦截。然后在拦截器内部启动复制均衡策略
8)可用自定义负载均衡策略吗?可用。
Feign的远程服务调用
1)为什么使用feign(基于Feign可以更加友好的实现服务调用,简化服务消费方对服务提供方方法的调用)
2)FeignClient的注解的作用是什么(告诉Fegin,在项目启动的时候,为此注解描述的接口创建实现类,代理类)
3)Feign方式的调用,底层负载均衡是如何实现的(Ribbon)
4)EnableFeignClients注解的作用是什么?(描述启动配置类。开服务调用)
配置中心
1)、什么是配置中心。(存储项目配置信息的一个服务)
2)、为什么要使用配置中心。(集中管理配置信息,动态发布配置信息)
3)、配置中心一般都会配置什么内容,(经常变化的配置信息,连接池,限流规则)
4)、什么信息一般不会写到配置中心(服务端口,服务名,服务的注册地址,配置中心)
5)、项目中为什么要定义bootstrap.yml文件?(此文件读取的优先级比较高,可以在服务启动的时候去读取配置中心的配置)
6)、Nacos配置中心宕机了,我们的服务还可以读取到配置信息吗?(可以。客户端获取配置中心的配置信息后,会将配置信息在本地存储一份)
7)、微服务应用中我们的客户端如何从配置中心信息(我们的服务一般会先从内存中读取配置信息,同时我们的的微服务还可以定时想nacos配置中想法请求拉取更的配置信息)
8)、微服务应用中客户端如何感知配置中心的数据变化?(nacos客户端会基于长轮询机制从nacos获取配置信息,所谓的长轮询就是没有配置更新是,会在nacos服务端的队列进行等待)
9)、服务启动后没用从配置中心获取到我们配置数据是什么原因(依赖,配置文件名字bootstrap.yml,配置中心的DataId名字是否正确,书写格式是否正确,动态发布的话。类上有没有@RefresScope注解)
10)、项目中的日志级别(deBug,info,error,)
11)、Nacos客户端是否可以读取共享配置:可以
58、锁
1)sync作用在静态方法和非静态方法的区别
作用在静态方法上是锁的对象Class
作用在非静态方法上锁的是这个实例对象
2)线程通过有几种方式
sync修饰方法或者代码块
lock代码块
使用redis获取分布式锁
如果是集合的可以使用并发的集合类
3)sync和lock的区别
相同是都能保证线程安全的
不同就是:sync是关键字,lock是一个接口ReentranLock
sync可使用在方法上或代码块。lock使用在代码块上
sync自动释放锁,lock需要手动释放锁
4)乐观锁和悲观锁
什么是乐观锁:就是乐观的任务当你去操作这个数据的时候别人不会修改这个数据。但是拿到值后需要再次判断比较一下。如版本号,CAS 比较和交换。
什么是版本号:如原先值是1号版本。当你去修改后,发现是原来的版本就提交修改。如果不是就需要重新查询再次修改。
什么是CAS 全称就是比较和交换,内存中的值,和比较的值,以及需要写入的值。
只有当内置中的值等于比较的值时,CAS通过原子方式用新值更新内存中的值。否则不会进行替换。
什么是悲观锁:就是悲观的任务当你去操作这数据的时候别人会修改这个数据。如行锁,表锁
5)什么是行锁什么是表锁
行锁又分为共享锁和排他锁,也即读锁和写锁
读锁就是:当我用读锁的方式访问几行数据的时候,别人可以共享读这个数据,但是不能修改这个数据(上读锁 lock in share mode)
写锁就是:当我操作这几行数据的时候。不许其他人进行更改操作。但是允许读(for update)
行锁必须有索引才能实现。否则会自动锁全表
两个事务不能锁同一个索引
增,删,改在事务中都会自动默认加上排他锁
区别
表锁:不会出现死锁,发送锁冲突概率高,并发低
行锁:会出现死锁,发送锁冲突概率第,并发高。
锁共存:
读锁可以共存,读写,写写不能共存。