(最高本版)小问

123 阅读18分钟

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的一种

  1. 只要涉及到基本数据类型,一直表示的是值比较。也就是说,只要==号左边或者右边有一个是基本数据类型,那么就是值比较。

  2. 如果==号左右两边都是包装类,那么==号表示引用类型所指地址是否一致。 注意: 因为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的构造器,其他包装类也是一样的。

  3. 包装类的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的作用域