基础技术
ssm
Java核心技术 io 多线程 集合 常规sql
Javaweb
我们为什么做服务化?
高可用,高扩展,高性能
特点:适合业务复杂的拆分场景,更灵活做子系统升级等等,系统的之间通信,变成为进程间的通信
服务拆分原则
根据业务边界拆分+公性基础服务
服务拆分后如何治理
抽取公共的基础服务
以业务为边界,拆分服务
管理服务,注册中心
服务之间通信方式
1.同步调用
Dubbo, RPC的方式,底层采用Netty来实现。基于TCP建立的是长连接
注意: BIO, NIO只是一 种网络通信模式 I
BIO:为每个连接创建一个线程
NIO: 一个线程服务多个连接,有弊端,当连接数太多,性能下降I
Netty:封装了NIO,在它的基础上,添加主从处理组,BossGroup. WorkerGroup
编程模型: Reactor
SpringCloud, restful http的方式
调用方需要等待执行方的处理结果
2,异步通知
MQ,调用方无需等待执行方的处理结果
面向对象的思维
面向对象的思维是以一个组织者的身份去思考问题,我需要一个什么功能,我只需要找到那个专业的工具类就好了,我的责任是去负责调用其中方法。
封装:
把使用者和设计者分开,只需要知道怎么使用无需知道怎么去实现。有利于提高类和系统的安全性。
你可以不知道手机怎么通话的,知道怎么打电话就行了。
继承:
继承指的是建立一个新的派生类从一个或多个先前定义的类中继承数据和函数。同时可以定义或加进新的数据和函数,从而建立等级和层次。为了更好地复用性
爷爷有一个花瓶,孙子不可以越过爸爸继承爷爷的花瓶 ,爸爸可以继承爷爷的花瓶,孙子可以通过继承爸爸的花瓶来继承花瓶。
多态:
多态是指同一操作作用不同的实例产生的结果就不同。
上课铃响了,体育生去操场,上语文课的回教室。
JDK&JRE&JVM
JVM通过不同版本不同位数的虚拟机屏蔽开发 差异化,提供统一的接口。
面试基础问题 ==和equals的区别
==比较的是值
比较基本的数据类型,比较的是数值
比较引用的数据类型,比较的是地址值。
equals默认比较地址,最初定义在object上默认的实现就是比较地址
自定义的类,需要比较内容,那么就需要重写equals方法
final修饰类
表示类不可变不可继承。
修饰方法,方法不可变。常用的固定算法模板。
修饰变量,这个变量就是常量。
修饰基本数据类型这个值不可以改变
修饰的引用数据类型这个引用的指向不可以改变。
可以改变指向区域的数值,不可以改变指向的区域。
面试基础问题之string stringbuffer stringbuilder
string和其他两个类的相同点
都是同来操作字符串的
string和其他两个类的区别
string是final类型,声明不可变。每次操作产生指向新的string对象。stringbuffer stringbuilder如果需要操作字符串则建议采用这两种
stringbuffer VS stringbuilder
安全方面:前者线程安全,后者线程不安全。
判断条件:
多线程环境下,对这个对象的访问不需要加入额外的同步控制操作数据的结果依然是安全的。
stringbuffer每个方法都加了关键字synchronize
stringbuilder每个方法都没有关键字的修饰
性能方面:后者性能优于前者性能。
多线程访问同一个资源。需要考虑安全问题。
单线程访问资源,不需要考虑线程安全问题。
开发实例
解决字符拼接问题。
接口和修饰类的区别
JDK1.8之前
语法:
抽象类:方法可以使抽象的,也可以是非抽象的,有构造器。
接口:方法都是抽象的属相都是常量,默认有public static final修饰
设计:
抽象类:同一类事务的抽取,比如只针对dao层的封装。
接口:同常更像一种表中的制定定制系统之间的对接标准。
JDK1.8之后
接口里可以有 实现的方法,需要在方法上声明default或者static我们的实现类可以选择性实现某些方法。
总结
1、接口类似于bai类,但接口的成员都没有执行方du式,它只是方法、属性、zhi事件和索dao引的组合而已,并且也只能包含这四种成员;类除了这四种成员之外还可以有别的成员(如字段)。
2、接口不可以被实例化,接口只包括成员的签名;而类可以实例化(abstract类除外)。
3、接口没有构造函数,类有构造函数。
4、接口不能进行运算符的重载,类可以进行运算符重载。
5、接口的成员没有任何修饰符,其成员总是公共的,而类的成员则可以有修饰符(如:虚拟或者静态)。
6、派生于接口的类必须实现接口中所有成员的执行方式,而从类派生则不然。
概念区分:
多继承,多重继承,多实现。
多重继承:A>B>C
多实现:person implement irunable.IEtable
多继承:接口可以 多继承类只能单继承
算法题N的阶乘
递归:
方法内部调用自身
注意事项:
找到规律,程序的公式
找到出口:(边界值 )让递归有结束边界。
- 当 n=0,1 时,结果正确;
- 假设递归对于 n 是正确的,同时对于 n+1 也正确。
提取重复逻辑,缩小问题规模
递归模型1:(递归过程中解决问题)
function recursion(大规模){
if (end_condition){ // 明确的递归终止条件
end; // 简单情景
}else{ // 在将问题转换为子问题的每一步,解决该步中剩余部分的问题
solve; // 递去
recursion(小规模); // 递到最深处后,不断地归来
}
}
递归模型2:(在归类过程中解决问题)
- function recursion(大规模){
if (end_condition){ // 明确的递归终止条件
end; // 简单情景
}else{ // 先将问题全部描述展开,再由尽头“返回”依次解决每步中剩余部分的问题
recursion(小规模); // 递去
solve; // 归来
}
}
注意问题:
递归太多层会出现栈内存溢出问题。
为什么是栈内存溢出不是堆内存溢出?
每次调用方法,会在栈内存画出区域(画出的区域叫栈帧,用于存储当前方法的变量)
建议:
建议在网上找一点题自己手写代码效果会好一点
int & integer概念
话不多说上代码
public class Main {
public static void main(String[] args) {
Integer i1=new Integer(12);
Integer i2=new Integer(12);
System.out.println(i1 ==i2);false
Integer i3 =126;
Integer i4 =126;
int i5 =126;
System.out.println(i3==i4);true
System.out.println(i3==i5);true
Integer i6 =128;
Integer i7 =128;
int i8 =128;
System.out.println(i6==i7);false
System.out.println(i6==i8);true
}
}
1:看边界有没有越界
2:引用类型比较地址值,基本类型比价值
3:int初始化为0 integer初始化为null
4:JDK1.5之后有了自动装箱和自动拆箱
5:二者不可以互用因为是不同的数据类型
方法重载和重写
重载发生在类里边,方法名相同,参数列表不同(混淆点:跟返回类型没有关系)
重写发生在子类和父类之间,方法名相同,参数列表不同。
案例:
以下案例是否构成重载? (不构成)
public double add(int a ,int b)
public int add(int a, int b)
list set 区别
list有序的可重复的
set无序的不可重复的 (所谓的无需是加进去的顺序不等于输出的顺序)
collections 工具类(Java工具类命名+s结尾) collection 接口 持续跟新…
ArrayList和linkedlist
ArrayList 数组结构 连续的内存空间
内存空间连续查找快,方便寻址。增删慢因为需要数据迁移。
linkedlist 链表结构 不是连续的内存空间
两个指针一个指向头部,一个指向尾部。
查找慢,需要通过指针一个个寻找,增删快 改变前后节点指向即可。
存储定量数据的时候那个更省内存?
ArrayList()长度固定连续的内存空间
linkedlist ()创建一个新数组,通过位运算长度扩容为1.5倍将原数组迁移到新的数组。
如何实现一个IOC容器
配置文件方式
注解方式
hashset存储原理
hashset底层使用hashmap来实现存储其值作为hashmap的key
为什么采用hash算法?有什么优势,解决了什么问题?
解决唯一性问题。,存储数据底层采用数组,当我们数组存储数据的时候判断是否唯一。我们存储进来的数据经过hashcode得到一个int值在和 数组的长度做一个位运算数组长度-1得到我们要 存储在数组的那个下表下。如果位置没有有其他的元素直接存储。不同对象出现相同的值出现hash碰撞,用equal比较
所谓的hash表示怎样一张表?
hash表本质是一个数组 ,数组的元素是一个链表。JDK1.8以后链表会优化为红黑树。
hash如何保证对象的唯一性?经历了怎样一个运算过程?
ArrayList vs vetor
ArrayList 内部没有锁
vetor 内部有锁
安全的对象,在多线程情况下,不需要 额外的控制依旧可以保证线程安全。
hashtable hashtable collectionssynchronizemap
hashtable 线程安全的对象,内部有上锁控制。
hashmap 线程不安全 多个线程同时操作同一个hashmap可能出现不安全情况甚至初选死锁。
collectionssynchronizemap工具类提供了同步包装器方法来 返回线程安全集合对象(性能依旧有问题)
给我一个线程不安全的线程,加工为一个安全线程。返回。
优先选择hashmap不是多个线程访问同一个资源情况优先选择hashmap局部变量而不是全局变量。
开发一个自己的栈
1:数组
2:先进后出
3:出栈 取末尾元素() array【index-1】
4:入栈 默认加到末尾效率高 array【index++】=newobject
class node{
string pre;
string next;
T data;
}
io流
按照方向:
输入流:输入流式得到数据
输出流:输出流是输出数据
处理 方式:
节点流:可以从或向一个特定的地方(节点)读写数据
处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。
读取单位:
字节流:读取任何文件
字符流:读取文本信息
serialversionuid作用是什么?
当执行序列化时我们对象写入磁盘根据当前类结构生成一个版本ID反序列化时候程序比对版本ID是否一致如果一致则成功,不一致则失败。
加版本号有助于我们当前类发生变化,依然可以在之前序列化的对象反序列化成功。
Java异常体系(健壮性)
throwable (Java虚拟机异常)
栈内存溢出:stackovererror
堆内存溢出:
excepion
runtimeexeception:运行时异常(数组越界,空指针)
非运行时异常:Java try
throw跟throws区别
throw作用方法内,主动排除异常
throws:作用方法声明上,声明该方法有时候 会 抛出异常;
针对项目异常处理层层上抛出最终同意处理:
int result = finalyTest(;
System. out. println(result);
}
public static int finalyTestO {
try{
int i = 1/0;
} catch(Exception e) {
return 1 ;
}finally{
return 2;
}
创建线程方式
继承Thread
实现Runable接口
实现alble接口(可以获取线程执行之后的返回值)
public static void main(String[] args) {
MyThread thread = new WyThreadO:
//正E确G动线部的方式
Thread. run://调用方法并非开启新找程
}
I
class XyThread extends Thread{
COverride
public void run{
Systen out. println(Thread. currentThread0. getName0+* : runnin… ):
}
启动线程方式:
启动一个线程是调用start()方法线程进入就绪状态等待CPU执行,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行,这并不意味着线程就会立即运行。run()方法是线程启动后要进行回调(callback)的方法。
线程生命周期:
new (创建)runnable(就绪)blocked(等待锁) waiting(阻塞)timewaiting(调sleep) ,terminated
1,当进入synchronized同步代码块或同步方法时,且没有获取到锁,线程就进入了blocked状态,直到锁被释放,重新进runnable状态
2,当线程调用wait或者join时, 线程都会进入到waiting状态,当调用notify或notifyAll时, 或者join的线程执行结束后,会进入runnable状态
3,当线程调用sleep(time), 或者wait(time)时, 进入timed waiting状态,当休眠时间结束后,或者调用notify或notifyAll时 会重新runnable状态。
4,程序执行结束,线程进入terminated状态
线程安全理解
场景一:多个线程访问同一个资源,上锁,保证资源安全。比如stringbuilder借助外力加synchronized关键字
场景二:每个线程访问各自的资源不考虑线程安全问题。
sleep和wait的区别
所属的类不同:
sleep 定义在thread上
wait定义在object上
对锁的处理:
sleep不会释放锁
wait会释放锁
使用范围:
sleep适用任何代码块
wait必须在同步代码块执行
生命周期
1,当线程调用wait0或者join时 ,线程都会进入到waiting状态,当调用notify或notifyAll
时,或者join的线程执行结束后,会进入runnable状态
2,当线程调用sleep(time), 或者wait(time)时, 进入timed waiting状态,
为什么wait必须写在同步代码块中?
原因是避免CPU切换到其他线程,而其他线程又提前执行了notify方法,那这样就达不到我们的预期(先wait再由其他线程来唤醒),所以需要一个同步锁来保护
某个特定类加载器在接到加载类的请求时,就会逐级向上递归查找parent类加载器是否加载过这个类,如果找到顶都没有加载过才是尝试亲自加载该类,如果自己也找不到加载资源就抛出NoClassDefFoundError,每个Class的实例都会记得是哪个ClassLoader加载它的,可以由Class的getClassLoader()取得加载该类别的ClassLoade
双亲加载机制的意义
避免同一个类被重复加载,我们可以利用这个类加载机制来实现一个自定义的类加载器来加载诸如自定义的java.lang.System类等奇巧淫技。但是,在JVM中,判断一个对象是否是某个类型时,如果该对象的实际类型与待比较的类型的类加载器不同,那么会返回false。
ajax工作原理:
早期以xml为主要的传输数据格式,所以阿加西的最后一个字母代表XML现在以json为主。
jsp和servelt
jsp本质就是一个servlet
jsp工作原理
jsp–》翻译–》servlet–》翻译–》class最终文件
应用
jsp–》HTML+Java(实现视图)
servlet=Java+HTML
各取所长,jsp的特点在于实现视图,servlet的特点在于控制逻辑
servlet生命周期:
servlet是单例的
生命周期
创建对象–》初始化对象–》调用service内部判断()–》doxxx()–》销毁
创建对象的时机:
默认情况第一次访问servlet时创建
通过配置web.XML改变创建时机,在容器启动的时候创建
执行次数
对象创建只有一次。
初始化一次
销毁一次
需要考虑线程安全么?
构成不安全的因素
多线程环境
多个线程共享一个资源
这个单例对象是有状态的(在servlet方法中采用全局变量该运算结果作为下一步操作的判断依据)
session和cookie的区别
存储位置
session存储在服务端
cookie存储在客户端
数据格式不同
session:value为对象,object类型(session存一个对象,对象内部做存储和序列化)
cookie:value为字符串,存储一个对象需要将对象转换为json
存储数据的大小
session:收服务器内存控制
cookie:一般来说最大为4k(有差别)
生命周期
session:默认30分钟,关闭浏览器session不会消失
cookie:客户端控制的
情况1:随着浏览器关闭消失
请款2:非会话级,通过是指控制有效期.setmaxage
cookie其他配置
HTTPonly=true:防止客户端xss攻击
path=/ 访问路径
domain=设置域名
cookie跟session关系
HTTP协议是一种无状态协议,服务器为了记住用户状态采用session机制,服务器自动生成会话级coolie来保存session标识。
转发和重定向
转发:
在服务器内部发生跳转,知识之中一次请求保存在request对象 中的数据可以传递。
重定向:
发生在客户端,多次请求,多次请求之间传递数据,需要用session。
三层架构
MVC
web:负责与用户交互并提供外部接口
业务逻辑层:实现业务逻辑模块
获取数据层:将业务逻辑层处理持久化,方便后续查询
每一层都有自己的框架
web:springMVC
model:模型代表获取存取数据的对象Javaopojo
view:视图层代表包含数据可视化HTML jsp
controller:控制器控制数据流向
业务逻辑层:spring
数据持久层:hibernate,mybatis, springdatajpa,springjdbc
JSP常用内置对象的使用
并发和并行
并发:同一个CPU执行多个任务
并行:在多个CPU同事处理多个任务
数据库设计三范式反范式
第一范式:列不可分
第二范式:要有主键
第三范式:不可存在传递依赖
反范式:查询效率高
聚合函数:
手写:
表连接查询:手写
如何防止sql注入:
采用预处理对象:采用preparestatement对象而不是statement对象
可以提高执行效率,因为预先编译执行。
sql执行过程:语法校验-》编译-》执行
mybatis如何sql注入
mybatis设置输出sql
jdbc如何控制任务边界
事物的特性
acid
原子性(基础)一致性 约束条件)隔离性(手段) 持久性()
原子性:事务是数据库数据在事务操作前后都必须满足的业务规则约束。
一致性:数据库在事务操作前后都必须满足的业务规则约束
隔离性:一个事物不能被其他事务所干扰,同一个事务内部操作及使用的数据对其他事务是隔离的,并发执行各个事务之间是不能互相干扰的。
持久性:事务一旦提交结果是永久性的及时发生宕机依然可以依靠事务日志完成事务的持久化。
事务重做。当我们通过事务修改数据的时候数据库变化的信息记录到日志中然后对数据库中的数据进行修改。及时数据库系统发生奔溃我们还可以通过重做日志进行数据恢复。
事务的隔离级别
读为提交:脏读(读取错的数据),不可重复读,幻读
读已提交:避免脏读(不可重复读,幻读有可能发生)
可重复读:可避免脏读,不可重复读,(幻读有可能发生)
串行化:避免脏读,不可重复读,幻读
特别说明:
幻读:本地事务查询只有三条,执行发生时会有4条。
synchronize和lock区别
作用位置不同:
synchronize可以给代码方法加锁。
lock只能给代码块加锁。
锁的获取和释放机制不同
synchronize无需手动获取和释放发生异常会自动上锁不会出现死锁。
lock需要自己加锁和释放锁如果忘记释放锁就会出现死锁。
补充:
synchronize修饰成员方法的时候,默认锁当前对象。
synchronize修饰静态方法的时候默认锁对象是class对象
网络编程
TCP和udp的区别
两者都是传输层协议
tcp提供可靠的传输协议,传输需要建立连接传输慢
udp无法提供传输的可靠性,无需创建连接,以报文方式传输,效率高。
什么是死锁?怎么防止
线程A独自占有资源a,并尝试获取独占资源b。同时线程B独占锁资源b。并 尝试获取独占锁资源a。互相持有对方需要的锁。从而发生加塞。
如何防止
尽量采用trylock方法,是指超时时间,超时后主动退出。
减少同步代码块嵌套操作
降低锁使用粒度,不要几个功能共用一把锁。
反射
反射是一种能力。一种在程序运行时,动态获取当前当前类对象的所有属性和方法的能力,可以动态的执行方法,给助兴赋值等操作能力。
反射就是让Java程序具备动态性。
什么是反射,解决什么问题
1:可以对任何一个类动态的获取这个类的属性和方法
2:对于任意一个对象都可以调用他的任意一个方法和属性
这种动态获取信息及调用对象的方法功能称之为反射
你对spring的认识
核心IOC容器(控制翻转)帮助我们自动管理依赖,不需要我们自己创建和管理依赖对象,实现层与层字节的解耦。
aop(面向切面编程)方便我们将一些非核心业务抽取出来,将核心业务和非核心业务进行解耦。非核心业务例如(事务管理,日志,性能检测,日志,读写分离)等等
spring的bean作用域
默认singleton,单例模式
prototyep,每次调用bean都需要创建一个洗呢对象,比如整合string2的时候,spring管理action对象都需要设置。
request,每次请求HTTP请求都会请求创建一个对象。
session:同一个session共用一个对象。
global-session
spring的bean是线程安全的么?
1:多线程环境
2:访问同一个资源
3:资源具有状态性(所谓的状态性就是没有存储数据没有通过数据的状态作为下一步的操作判断依据)
那么spring的bean模式是单例模式,天然处于一个多线程环境。
安全的么?
bean是无状态的从资源的状态性来说是安全的。
事务的传播特性及spring支持特性有哪些?
什么是事务的传播特性?
一般讲事物的边界设置在service层。
那么当我们调用Service层的一一个方法的时,它能够保证我们的这个方法中执行的所有的对数据库的更新操作保持在一个事务中,在事务层里面调用的这些方法要么全部成功,要么全部失败。那么事务的传播特性也是从这里说起的。
如果你在你的Service层的这个方法中,还调用了本类的其他的Service方法,那么在调用其他的
Service方法的时候,这个事务是怎么规定的呢?
必须保证在我方法里调用的这个方法与我本身的方法处在同一个事务中, 否则无法保证事物的一致性。
spring支持事务传播特性
在Spring中,针对传播特性的多种配置,我们大多数情况下只用其中的一种:PROPGATION REQUIRED:
这个配置项的意思是说当我调用service层的方法的时候,开启一个事务,
那么在调用这个service层里面的其他的方法的时候如果当前方法产生了事务就用当前方法产生的事务,否则就创建一个 新的事务。
这个工作是由Spring来帮助我们完成的。
3, Spring支持的事务传播特性
PROPAGATION_ REQUIRED: 支持当前事务,如果当前没有事务,就新建-一个事务。 这是最常见的选择。
PROPAGATION_ SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION MANDATORY:支持当前事务,如果当前没有务就抛出异常。
PROPAGATION_ REQUIRES_ NEW:新建事务,如果当前存在事务,把当前事务挂起
PROPAGATION NOT_ SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_ NEVER: 以非事务方式执行,如果当前存在事务,则抛出异常。
synchronize的 底层原理
JDK1.6之前是由两个指令,一个自动上锁,一个自动解锁同步实现。jdk6之后提供了monitor实现方式分别是偏向锁,重量级锁,轻量级锁。锁会根据情况逐步升级。
升级触发条件:
用户态
在锁对象连有一个threadID字段。默认为空,当第一次有线程访问时则将该threadid设置为当前线程id,我们称之为获取偏向锁。当线程结束threadid设置为空。
如果线程再次进入,判断threadid是否与该线程id是否一致,如果一致则获取该对象,不一致则锁升级,升级为轻量级锁。
内核态
轻量级锁的工作模式是通过自旋循环的方式来获取锁,看对方线程是否已经释放了锁,如果执行一定次数之后,还是没有获取到锁,则发生锁升级,从轻量级锁升级为重量级锁。
synchronize和volatile
1,作用的位置不同
synchronized是修饰方法,代码块
volatile是修饰变量
2,作用不同
synchronized,可以保证变量修改的可见性及原子性,可能会造成线程的阻塞
volatile仅能实现变量修改的可见性,但无法保证原子性,不会造成线程的阻塞
volatile
volatile是一个轻量级的线程同步机制。它的特性之一,是保证了变量在线程之间的可见性。
所谓的可见性是指当一个线程修改了变量的值之后,其他线程可以感知到该变化。为什么会有可见性问题?
是因为由于硬件速度的不同,CPU的速度要明显快于主内存。
所以为了解决速度不匹配的问题,在CPU到主内存之间就会有多级的缓存。
那么这个时候就会发生,一个线程修改了数据,数据还没有及时刷到主内存,那么其他线程读取到 的数据就依然还是旧的,这就是可见性问题发生的根源。
通过给变量设置volatile关键字修饰,可以保证变量在线程修改完之后,会刷新到共享内存,这样 其他线程就可以读取到最新的内容
volatile保证了在多个线程之间是可见的,但不能保证原子性操作。
volatile vs synchronized
synchronized也是保证了线程的可见性,同时也具备了多线程之间的互斥性
如何使用volatile ?
直接修饰变量即可
private volatile int count,
volatile底层实现原理
当变量被声明为volatile后,线程每次都会都从主内存去读取,而不是读取自己的工作内存,这样就实现了线程之间的可见性
服务注册与发现的原理
所谓的服务注册,就是在zookeeper的服务器上创建-个节点, 而且是临时节点,保存着服务的地址信息。
所谓的服务发现,就是去获取zookeeper,上面的节点信息,获取到提供该服务的地址列表信息
这样当消费者去调用服务提供者,就可以采用负载均衡策略,去访问其中-个提供者。
监听机制,当服务提供者某个节点发生故障,这个时候服务端的临时节点会被删除,上层的父节点就相当发生了变化,所以可以基于这个机制通知客户端(服务消费者)当前服务列表发生变化了,客户端再次去获取最新的服务列表信息。
监听节点是否变化,发生变化通知dubbo,默认情况下有本地列表,收到节点变化信息变化通知dubbo客户端rpc框架自动抓取最新列表。(zookeeper奔溃也可以通信,有缓存)
服务之间共享资源,如何保障数组安全
单体架构,采用jdksynchronize可以实现
分布式架构:多机部署jvm(分布式锁)
控制范围是一个jvm,但分布式是n个资源所以控制不了。
分布式锁:
1上锁
2解锁
数据库方式:
t-lock
id lock0
上锁数值修改为1
解锁数值修改为0
Redis
setnx:
不存在,就设置成功,否则,设置失败
上锁:成功执行setnx key value
解锁: delete key
避免死锁:需要设置过期时间expire key timeout
注意:需要将两个操作变成一个原子操作
4.0之前:
Redis+lua脚本,lua脚本帮助我们扩充Redis指令
避免无锁:
释放锁的时候,检查这把锁是不是我的
zookeeper
以节点为锁,创建成功,表示成功获取锁。
上锁:创建节点lock
解锁:删除节点lock
避免死锁:节点为临时类型,客户端挂掉,自动删除
死锁:设置过期时间
setnx lock uuid
expire lock 1000
当变量被声明为volatile后,线程每次都会都从主内存去读取,而不是读取自己的工作内存,这样就实现了线程之间的可见性
服务注册与发现的原理
所谓的服务注册,就是在zookeeper的服务器上创建-个节点, 而且是临时节点,保存着服务的地址信息。
所谓的服务发现,就是去获取zookeeper,上面的节点信息,获取到提供该服务的地址列表信息
这样当消费者去调用服务提供者,就可以采用负载均衡策略,去访问其中-个提供者。
监听机制,当服务提供者某个节点发生故障,这个时候服务端的临时节点会被删除,上层的父节点就相当发生了变化,所以可以基于这个机制通知客户端(服务消费者)当前服务列表发生变化了,客户端再次去获取最新的服务列表信息。
监听节点是否变化,发生变化通知dubbo,默认情况下有本地列表,收到节点变化信息变化通知dubbo客户端rpc框架自动抓取最新列表。(zookeeper奔溃也可以通信,有缓存)
服务之间共享资源,如何保障数组安全
单体架构,采用jdksynchronize可以实现
分布式架构:多机部署jvm(分布式锁)
控制范围是一个jvm,但分布式是n个资源所以控制不了。
分布式锁:
1上锁
2解锁
数据库方式:
t-lock
id lock0
上锁数值修改为1
解锁数值修改为0
Redis
setnx:
不存在,就设置成功,否则,设置失败
上锁:成功执行setnx key value
解锁: delete key
避免死锁:需要设置过期时间expire key timeout
注意:需要将两个操作变成一个原子操作
4.0之前:
Redis+lua脚本,lua脚本帮助我们扩充Redis指令
避免无锁:
释放锁的时候,检查这把锁是不是我的
zookeeper
以节点为锁,创建成功,表示成功获取锁。
上锁:创建节点lock
解锁:删除节点lock
避免死锁:节点为临时类型,客户端挂掉,自动删除
死锁:设置过期时间
setnx lock uuid
expire lock 1000
lua脚本–原子性操作