java乐观锁和悲观锁 的区别
如何跳出多层的循环嵌套?
使用 break label(标签)
list排序
对象实现comparable类,再执行集合的排序Collections.sort(list);
mq问题
类加载的顺序
1.加载静态成员/代码块:先递归地加载父类的静态成员/代码块(Object的最先);再依次加载到本类的静态成员;如果其间调用静态方法,则调用时会先运行静态方法,再继续加载
2、加载非静态成员/代码块:(实例块在创建对象时才会被加载。而静态成员在不创建对象时可以加载)
先递归地加载父类的非静态成员/代码块(Object的最先);再依次加载到本类的非静态成员。 同一个类里的非静态成员/代码块,按写代码的顺序加载
3、调用构造方法: 先递归地调用父类的构造方法(Object的最先);默认调用父类空参的,也可在第一行写明调用父类某个带参的。
再依次到本类的构造方法;构造方法内,也可在第一行写明调用某个本类其它的构造方法。
线程的run和start
通过调用Thread类的 start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,
linux编辑文本指令
cat 由第一行开始显示文件内容
vi和vim编辑文本
查看网路状态:
service network status
stat命令显示文件或目录的详细属性信息包括文件系统状态
Dubbo服务暴露过程 :
首先serviceConfig类拿到对外暴露服务的实际类ref,然后通过proxyfactory类的getInvoker方法生成AbstractProxyInvoker 实例,然后Invoker转换到 Exporter,实现服务暴露
服务的暴露过程: 暴露本地服务,暴露远程服务,启动netty,连接zookeeper,到zookeeper注册,监听zookeeper
一句话概括服务暴露: Service->Invoker->Exporter
简易的暴露流程 首先将服务的实现封装成一个Invoker,Invoker中封装了服务的实现类。 将Invoker封装成Exporter,并缓存起来,缓存里使用Invoker的url作为key。 服务端Server启动,监听端口。(请求来到时,根据请求信息生成key,到缓存查找Exporter,就找到了Invoker,就可以完成调用。)
Rabbit如何保证消息不丢失 1、消息持久化 要想做到消息持久化,必须满足以下三个条件,缺一不可。
Exchange 设置持久化
Queue 设置持久化
Message持久化发送:发送消息设置发送模式deliveryMode=2,代表持久化消息
2.ACK确认机制
在消费一条消息的过程中,可能消费者宕机,此时,这条数据没有处理完成,就回丢失。如果在队列中设置一个,获取了消费者的去人后,才删除这条消息,就确保数据不会丢失。
3.设置集群镜像模式
下面介绍下三种HA策略模式:
1)同步至所有的 2)同步最多N个机器 3)只同步至符合指定名称的nodes
但是:HA 镜像队列有一个很大的缺点就是: 系统的吞吐量会有所下降
4.消息补偿机制 blog.csdn.net/weixin_4378…
RuntimeException是在Java虚拟机的正常操作期间可能引发的那些异常的超类。
NullPointerException是RuntimeException的一种
-
只要涉及到基本数据类型,一直表示的是值比较。也就是说,只要==号左边或者右边有一个是基本数据类型,那么就是值比较。
-
如果==号左右两边都是包装类,那么==号表示引用类型所指地址是否一致。 注意: 因为Integer这个包装类比较特殊,内部有一个数组数组,保存了-128到127的值,也就是Integer i = n,只要n在-128到127之间,都是直接从数组中获取,不会再创建新的对象。 对于其他的包装类,比如Double d = 2.1;那么2.1会被自动装箱,也就是调用valueOf方法,这个方法内部还是new了一个Double对象的。 同时需要注意,自动装箱可不提供向上转型,也就是Double d = 1;1不会先自动向上转成double然后再装箱,会直接编译错误,因为Double这个包装类中就没有形参是int的构造器,其他包装类也是一样的。
-
包装类的equals方法,不提供类型转换。例如Byte b = 100;Integer i = 100;i.equals(b);返回false。 1. equals方法先比较类型,再比较值。
小米面试 一面
1、常用Java集合类。
(1)Java 中的集合主要分为四类:
•List 列表,有序,可重复;
•Queue 队列,有序,可重复;
•Set 集合,不可重复;
•Map 映射,无序,键唯一,值不唯一每种集合类型下都包含多个具体的实现类;
2、HashMap为什么长度是2的n次幂, 为了让数据均匀存储,减少数据的hash碰撞
数据结构,
数据的位置时hash后的值,也是链表的头节点
数组的长度必须是2的指数次幂
jdk1.8以前是数组+链表
jdk1.8以后是数组+链表+红黑色(平衡二叉树)
扩容(包括元素移动的细节),
线程不安全的问题:ConcurrentHashMap可以解决线程安全问题
3、ConcurrentHashMap是如何保证线程安全的?
这个方法是利用一个CAS算法实现无锁化的修改值的操作,他可以大大降低锁代理的性能消耗
4、1.7和1.8有什么变化?为什么要做这样的优化?
5、CopyOnWriteList怎么保证线程安全 CopyOnWrite容器进行并发的读,因为没有写操作,不存在线程安全问题;写的时候,是复制一份,增加需要添加的数据,写和读再不同的容器,相当于读写分离
写的过程:
首先会先将原来的数组拷贝一份并且让原来数组的长度+1后就得到了一个新数组,新数组里的元素和旧数组的元素一样并且长度比旧数组多一个长度,然后将新加入的元素放置都在新数组最后一个位置后,用新数组的地址替换掉老数组的地址就能得到最新的数据了
6、 synchronized关键字的作用,原理,锁升级、锁粗化、锁消除。
锁粗化:如果枷锁的分为很小,外部由循环,所以会频繁枷锁解锁;jvm检查到这个操作后,会粗化锁,使得锁在循环执行程序外;
锁消除:
StringBuffer 是线程安全的,被 synchronized 修饰过;sb 这个引用只会在 add 方法中使用,不可能被其它线程引用。所以内部的synchronize锁会被消除
7、volatile关键字的作用,原理。
实现数据的可见性;阻止代码重新排序
8、MVCC; 事务版本控制
9、事务的ACID,每一项是如何保证的?
3、tcp握手挥手过程,以及socket的状态变化。
6、synchronized修饰同一个类的两个静态方法同步吗,为什么? 可以,因为是对类进行加锁。产生互斥,因为对静态方法加锁,实际上是对类加锁,类只有一个。因此当一个同步静态方法被访问时,该类已处于被锁状态。此时其他同步静态方法不能被访问(未用synchronized修饰的静态方法仍可以访问)
10、镜像二叉树(递归和非递归)
11、删除二叉搜索树的某一个节点。
12、给定数组,求第k大的数字
11、手撕代码:二分查找;这道牛客题霸上有原题,大家可以去看看:NC105二分查找
12、手撕代码:反转链表;这道牛客题霸上也有原题,大家可以去看看:NC78反转链表
1、求递增数组中相加等于10的元素对。
2、17^400 - 19100计算结果能不能被10整除。
3、先升序后降序的数组排序。
tomcat发请求,没有返回值,问题排查
java实现公平队列锁的实现类
maven的命令
dubbo实现版本号切换的方法
dubbo和feign调用的区别
redies的缓存击穿如何处理
布隆过滤器 实现原理:对发来的请求,先使用多个hash算法,计算出位置,找出各个hash后的结果,查找key值是否为1,如果是,则可能存在该数据。在存储在键值对里面。从而过滤掉大部分的无意义的请求。
优点: 1、增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关
2、哈希函数相互之间没有关系,方便硬件并行运算
3、布隆过滤器不需要存储元素本身,大数据量,可以避免内存的消耗,在可以承受一定的误判下,它有很大的空间优势
缺点: 1、有误判率
2、不能获取元素本身
3、一般情况下不能从布隆过滤器中删除元素(可能将其他的数据也删除了)
hash算法:
把一个大范围映射到一个小范围
hash算法要做到数据均匀分布,计算简单
常见 Hash 算法有 MD5 和 SHA 系列,目前 MD5 和 SHA1 已经被破解,一般推荐至少使用 SHA2-256 算法。这些算法可以实现加密,因为输入密码,计算计算以后,人工无法查看,如果加上盐值,就更不无法破解,
简单的hash算法:
除留余数法:散列表中允许的地址数为m,取一个不大于m,但接近或者等于m的质数p作为除 数,按照哈希函数:Hash(key) = key % p p<=m,将关键码转换成哈希地址。
平方取中法: 假设关键字是1234,那么它的平方就是1522756,再抽取中间的3位就是227作为散列地址;再比如关键字是4321,那么它的平方就是18671041,抽取中间的3位就可以是671或者710用作散列地址。 平方取中法比较适合:不知道关键字的分布,而位数又不是很大的情况。
折叠法: 将数据分成几段,进行相加
优点:冲突较小
缺点:质数(素数)p的值较难取。
hash碰撞:不同的值计算的结果相同
加盐防碰撞:原来的明文加上一个随机数之后的 Hash 值,Hash 值和盐会保存在两个地方,只要不是同时泄漏就很难被破解
redis各版本的主要特性和稳定的版本
3.XX 增加了Redis的分布式实现Redis Cluster
4.XX 提供了RDB-AOF混合持久化格式,充分利用了AOF和RDB各自优势。
5.XX 新的Stream数据类型
6.XX版本 比较稳定的版本
Redis 6引入多线程IO,但多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程。之所以这么设计是不想因为多线程而变得复杂,
ACL权限控制 1、支持对客户端的权限控制,实现对不同的key授予不同的操作权限。
支持SSL Redis 5之前的版本不只是数据在传输过程中进行加密,Redis 6支持了通道加密的功能,使得Redis更加安全。
redies实现延时队列
组成:存储消息、延迟队列、延迟执行器。
消息存储使用
hash结构能存储较大的数据量,数据较多时候会进行渐进式rehash扩容
延迟队列:是16个有序队列(队列支持水平扩展),结构为ZSET,value为messages pool中消息ID,score为过期时间(分为多个队列是为了提高扫描的速度) zset的特性:是一个有序的不重复集合,通过score存储过期时间,可以对set进行排序
延迟执行器:定时任务,负责扫描处理每个队列过期消息,如果有,就消费队列上的消息,消费完后,删除队列的id和消息存储器上面的值
rabbitMQ实现延时队列
实现需要设置:死信交换机(Exchange)和消息的存活时间TTL(Time To Live)
当消息满足一下要求会进入死信交换机(死信交换机就是普通的交换机,只是因为我们把过期的消息扔进去):
1、消息被消费者拒收
2、消息的存活时间TTL已到
3、队列的长度限制满了。排在前面的消息会被丢弃或者扔到死信路由上。
总结: 将消息设置一个过期时间TTL,放入一个没有读取的队列中,让消息过期后自动转入另外一个队列(死信交换机实现),监控这个队列消息的监听处来处理定时任务具体的操作
redis的内存结构
Redis是典型的Key-Value类型数据库,Key为字符类型,Value的类型常用的为五种类型:String、Hash 、List 、 Set 、 Ordered Set
redisObject 对象:
上图为redisObject 对象:
type: value 对象具体是何种数据类型。
String(字符串类型的Value)
可以是String,也可是是任意的byte[]类型的数组
Hash(HashMap,哈希映射表)
hash内部是hashmap结构
下图为hash的存储结构
List(双向链表)
set(HashSet)
Set就是HashSet,可以将重复的元素随便放入而Set会自动去重,底层实现也是HashMap
实现原理
实际就是通过计算 hash 的方式来快速排重的,这也是 set 能提供判断一个成员是否在集合内的原因。
Sorted Set(有序的set集合)
set 不是自动有序的,而** sorted set 可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序**
实现方式
内部使用 HashMap 和跳跃表(SkipList)来保证数据的存储和有序
阻塞,非阻塞:
阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回
非阻塞,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
同步,异步:
按顺序做两件事情,如果可以同时做,就可以异步,如果先做完一件,再处理另一件,就是同步的(同步就是指一个进程在执行某个请求的时候,若该请求需要一段时bai间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。)
mysql版本5.7比较稳定
hashmap的实现原理
利用key的hashCode重新hash计算出当前对象的元素在数组中的下标 存储时,如果出现hash值相同的key,此时有两种情况。(1)如果key相同,则覆盖原始值;(2)如果key不同(出现冲突),则将当前的key-value放入链表中 获取时,直接找到hash值对应的下标,在进一步判断key是否相同,从而找到对应值。 理解了以上过程就不难明白HashMap是如何解决hash冲突的问题,核心就是使用了数组的存储方式,然后将冲突的key的对象放入链表中,一旦发现冲突就在链表中做进一步的对比。
分布式事务控制
产生的场景:
1、不同物理机器的jvm
2、单服务有多个数据库
3、多个服务访问同一数据库
事务的cap理论
c一致性,多个客户端获取的数据时一致的
特点:
1、写操作会有一定的延迟
2、为了保证数据的一致性,数据资源会锁定,直到同步完成才会释放资源
3、如果请求失败的节点,会返回错误信息,不会返回旧数据
a可用性:任何事务的操作都可以返回结果,且不会出现超时或者错误。
实现,
1、写入主服务的数据,需要同步到从服务
2、为了保证服务的可用性,不可以将服务器的资源锁定
3、获取服务数据,可以时旧数据或者默认值
p分区容错性 分布式系统,不可能保证所有的服务都正常,但是如果一个节点出问题,不影响其他服务的正常使用
强一致性和最终一致性的区别:
强一致性要保证客户端请求每一个服务端数据都是相同的
最终一致性最要服务最后数据一致即可
base理论:可用性、软状态、最终一致性
分布式事务的方案
2pc
由事务管理器和事务发起者组成
整个周期分为准备阶段和提交阶段,准备阶段把所有的条件都满足了,再执行提交
seata分布式框架
seata把一个分布式事务理解为包含若干分支事务和全局事务。
下图为seata框架的基本机构:
执行的流程:
组成:TM事务、RM资源、TC
TC:事务协调器,独立中间件,需要独立部署。它负责全局事务的运行,接收TM指令发起的全局事务提交,回滚,负责与RM通信协议协调各事务的提交回滚。
TM:事务管理器,TM需要嵌入到应用程序中工作,它负责开启一个全局事务,并最终向TC发起全局提交或全局的回滚指令。
RM:控制分支事务,负责分支注册,状态汇报,并接收事务协调器TC的指令,驱动分支(本地事务) 的提交和回滚。
seata的具体流程:
1、TM、RM分别注册到TC
2、TM开启全局事务
3、注册分支事务,返回分支事务id,写数据,写入undo_log,提交分支事务
4、上报分支事务处理结果到TC
5、springcloud通过feign将事务id传给下一个RM
6、下一个RM执行3
7、提交全局事务
8、删除各个RM自身的undo_log
9、中间如果出现问题,TC回滚事务
要点说明:
1、每个RM使用DataSourceProxy连接数据库,其目的是使得undo——log可以执行逆向回滚事务;
2、在第一阶段,已经提交事务,释放了锁资源,在回滚的时候,通过undo_log 实现回滚
3、TM开启全局事务开始,将XID全局事务id放在事务的上下文中,通过feign调用,将XID传到下游分支事务,每个分支事务将自己的branch ID分支事务ID和XID关联。
4、第二阶段全局事务提交,第二阶段全局事务的提交,TC会通知各个分支删除undo_log,可以异步执行。
5、第二阶段的全局事务回滚,TC会通知各个分支回滚事务,通过XID和Branch ID找到响应的回滚日志,执行反向sql。
seata常用的注解???
TCC
TCC要求每个分支事务都要实现三个操作预处理、确认、撤销。预处理是业务的检查和资源的预留,确认是事务的确认、撤销执行回滚的操作
hmily支持dubbo
TCC实现的产品, hmily的特性(需要开发try,confirm方法)
Hmily有一下特点:
本地事务存储支持:redis、mongodb、zookeeper、file、mysql
采用AspectAOP切面思想与Spring无缝集成,支持集群
空回滚:
分布式解决方案:
可靠消息最终一致性:
本地消息表方案: 发布式的事务,记录到本地的表中,通过轮询,将数据
GBK和UTF8有什么区别?
UTF8编码格式很强大,支持所有国家的语言,正是因为它的强大,才会导致它占用的空间大小要比GBK大,对于网站打开速度而言,也是有一定影响的。
GBK编码格式,它的功能少,仅限于中文字符,当然它所占用的空间大小会随着它的功能而减少,打开网页的速度比较快。
为什么要使用线程池:FixedThreadPool 线程池,线程已经提前创建好,不用频繁地去开启线程。线程的开启和回收的耗费资源的。
可运行状态--》运行状态--》阻塞状态--》死亡
aop的原理:
bean的作用域