Java核心技术一其他
1.分布式CAP, BASE知道吗?
CAP原则又称CAP定理,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼,只能至多满足其中2个。
BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent (最终一致性),BASE理论是对CAP中AP的一个扩展,除非特定的事务类型,大多数的系统都是AP系统,满足用户的可用性和服务的可用性,C只需达到最终一致性即可。
2.什么是二阶段提交,三阶段提交,两者有什么区别和优缺点
二阶段提交的算法思路可以概括为: 参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。
二阶段是指:
第一阶段 - 请求阶段(表决阶段) 预执行SQL
第二阶段 - 提交阶段(执行阶段) 根据一阶段执行结果 , 进行commit提交或者rollback回滚
缺点:
1.同步阻塞问题。执行过程中,所有参与节点都是事务阻塞型的。
当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。
2.单点故障。由于协调者的重要性,一旦协调者发生故障。
参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)
3.数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了commit请求。
而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据不一致性的现象。
两阶段提交无法解决的问题:
当协调者出错,同时参与者也出错时,两阶段无法保证事务执行的完整性。
考虑协调者在发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。
那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。
三阶段提交协议在协调者和参与者中都引入超时机制,并且把两阶段提交协议的第一个阶段分成了两步: 询问,然后再锁资源,最后真正提交。
三阶段的执行
(1) canCommit阶段
3PC的canCommit阶段其实和2PC的准备阶段很像。协调者向参与者发送commit请求,参与者如果可以提交就返回yes响应,否则返回no响应
(2) preCommit阶段
协调者根据参与者canCommit阶段的响应来决定是否可以继续事务的preCommit操作。根据响应情况,有下面两种可能:
a) 协调者从所有参与者得到的反馈都是yes:
那么进行事务的预执行,协调者向所有参与者发送preCommit请求,并进入prepared阶段。参与者和接收到preCommit请求后会执行事务操作,并将undo和redo信息记录到事务日志中。如果一个参与者成功地执行了事务操作,则返回ACK响应,同时开始等待最终指令
b) 协调者从所有参与者得到的反馈有一个是No或是等待超时之后协调者都没收到响应:
那么就要中断事务,协调者向所有的参与者发送abort请求。参与者在收到来自协调者的abort请求,或超时后仍未收到协调者请求,执行事务中断。
(3) doCommit阶段
协调者根据参与者preCommit阶段的响应来决定是否可以继续事务的doCommit操作。根据响应情况,有下面两种可能:
a) 协调者从参与者得到了ACK的反馈:
协调者接收到参与者发送的ACK响应,那么它将从预提交状态进入到提交状态,并向所有参与者发送doCommit请求。参与者接收到doCommit请求后,执行正式的事务提交,并在完成事务提交之后释放所有事务资源,并向协调者发送haveCommitted的ACK响应。那么协调者收到这个ACK响应之后,完成任务。
b) 协调者从参与者没有得到ACK的反馈, 也可能是接收者发送的不是ACK响应,也可能是响应超时:
执行事务中断。
三阶段提交协议的缺点
如果进入PreCommit后,Coordinator发出的是abort请求,假设只有一个Cohort收到并进行了abort操作,
而其他对于系统状态未知的Cohort会根据3PC选择继续Commit,此时系统状态发生不一致性。
3.Mysql的数据库优化方案
1.主从配置,读写分离
2.常用数据添加缓存
3.表结构设计上 及添加索引
4.SQL写法上
5.SQL执行上分析, 查看执行计划
4.分库分表做过吗? 什么业务场景? 分库分表带来的问题?
- 事务一致性问题 (性能要求很高,但对一致性要求不高的系统 采用最终一致性)
- 跨节点关联查询 join 问题 (1.采用全局表,如字典表很少修改的情况,每个库存一份 2.字段冗余, 典型的反范式设计. 3. 数据组装,在系统层面通过ID多次请求关联数据. 4.ER分片,确定表之间的关联关系,并将那些存在关联关系的表记录存放在同一个分片上.)
3、跨节点分页、排序、函数问题 (如果取得页数很大,情况则变得复杂)
4、全局主键避重问题 (1.UUID, 2.维护主键ID表, 3.Snowflake分布式自增ID算法)
5、数据迁移、扩容问题
5.反范式设计的缺点?
3NF(范式化): 原子性,惟一性,冗余性
优点
1· 可以尽量的减少数据冗余
2·数据表更新快体积小
3·范式化的更新操作比反范式化更快
4·范式化的表通常比反范式化更小
缺点:
1·对于查询需要对多个表进行关联,导致性能降低
2·更难进行索引优化
反范式化: 适当保留冗余数据, 以空间换时间
优点:
1·可以减少表的关联
2·可以更好地进行索引优化
缺点
1·存在数据冗余及数据维护异常
2·对数据的修改需要更多成本
6.Java反射的机制
反射是Java的特征之一,是一种间接操作目标对象的机制,核心是JVM在运行的时候才动态加载类,并且对于任意一个类,都能够知道这个类的所有属性和方法,调用方法/访问属性,不需要提前在编译期知道运行的对象是谁,他允许运行中的Java程序获取类的信息,并且可以操作类或对象内部属性。
- 获取class的三种方式
- Class.forName("className")
- obj.getClass()
- ClassName.class
- 优缺点
- 优点:使用反射,我们就可以在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
- 缺点:
(1)反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
7.你了解逃逸分析吗?
逃逸是指在某个方法之内创建的对象,除了在方法体之内被引用之外,还在方法体之外被其它变量引用到;
这样带来的后果是在该方法执行完毕之后,该方法中创建的对象将无法被GC回收,由于其被其它变量引用。
正常的方法调用中,方法体中创建的对象将在执行完毕之后,将回收其中创建的对象;故由于无法回收,即成为逃逸。
java Hotspot 编译器能够分析出一个新的对象的引用的使用范围,从而觉得是否需要将这个对象分配到堆上。
8.Java是解释执行这句话说法
静态编译指的是javac之类的编译,动态编译指的是JIT等即时编译。
9.线程池的核心参数有哪些? 他们的关系
corePoolSize : 核心线程数线程数定义了最小可以同时运行的线程数量。
maximumPoolSize : 当队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。
workQueue: 当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。
keepAliveTime:当线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁;
unit : keepAliveTime 参数的时间单位。
threadFactory :executor 创建新线程的时候会用到。
handler :饱和策略。饱和策略定义:
1.ThreadPoolExecutor.AbortPolicy:抛出 RejectedExecutionException来拒绝新任务的处理。
2.ThreadPoolExecutor.CallerRunsPolicy: 调用执行自己的线程运行任务,也就是直接在调用execute方法的线程中运行(run)被拒绝的任务,如果执行程序已关闭,则会丢弃该任务。
3.ThreadPoolExecutor.DiscardPolicy: 不处理新任务,直接丢弃掉。
4.ThreadPoolExecutor.DiscardOldestPolicy: 此策略将丢弃最早的未处理的任务请求。
10.NIO应用场景, Netty用过吗?
Java IO和NIO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的
Java IO的各种流是阻塞的。 面向流, 阻塞IO, 无选择器
Java NIO的选择器允许一个单独的线程来监视多个输入通道。 面向缓冲, 非阻塞IO, 选择器
Netty:
先来回顾一下NIO的通信步骤:
①创建ServerSocketChannel,为其配置非阻塞模式。
②绑定监听,配置TCP参数,录入backlog大小等。
③创建一个独立的IO线程,用于轮询多路复用器Selector。
④创建Selector,将之前创建的ServerSocketChannel注册到Selector上,并设置监听标识位SelectionKey.OP_ACCEPT。
⑤启动IO线程,在循环体中执行Selector.select()方法,轮询就绪的通道。
⑥当轮询到处于就绪状态的通道时,需要进行操作位判断,如果是ACCEPT状态,说明是新的客户端接入,则调用accept方法接收新的客户端。
⑦设置新接入客户端的一些参数,如非阻塞,并将其继续注册到Selector上,设置监听标识位等。
⑧如果轮询的通道标识位是READ,则进行读取,构造Buffer对象等。
⑨更细节的问题还有数据没发送完成继续发送的问题......
TCP粘包、拆包问题
熟悉TCP编程的可能都知道,无论是服务器端还是客户端,当我们读取或者发送数据的时候,都需要考虑TCP底层的粘包/拆包机制。
TCP是一个“流”协议,所谓流就是没有界限的遗传数据。大家可以想象一下,如果河水就好比数据,他们是连成一片的,没有分界线,TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的具体情况进行包的划分,也就是说,在业务上一个完整的包可能会被TCP分成多个包进行发送,也可能把多个小包封装成一个大的数据包发送出去,这就是所谓的粘包/拆包问题。
解决方案:
①消息定长,例如每个报文的大小固定为200个字节,如果不够,空位补空格。
②在包尾部增加特殊字符进行分割,例如加回车等。
③将消息分为消息头和消息体,在消息头中包含表示消息总长度的字段,然后进行业务逻辑的处理。
Netty中解决TCP粘包/拆包的方法:
①分隔符类:DelimiterBasedFrameDecoder(自定义分隔符)
②定长:FixedLengthFrameDecoder
数据通信
大体上分为三种:
①使用长连接通道不断开的形式进行通信,也就是服务器和客户端的通道一直处于开启状态,如果服务器性能足够好,并且客户端数量也比较上的情况下,推荐这种方式。
②一次性批量提交数据,采用短连接方式。也就是说先把数据保存到本地临时缓存区或者临时表,当达到界值时进行一次性批量提交,又或者根据定时任务轮询提交,
这种情况的弊端是做不到实时性传输,对实时性要求不高的应用程序中推荐使用。
③使用一种特殊的长连接,在某一指定时间段内,服务器与某台客户端没有任何通信,则断开连接。下次连接则是客户端向服务器发送请求的时候,再次建立连接。
在这里将介绍使用Netty实现第三种方式的连接,但是我们需要考虑两个因素:
①如何在超时(即服务器和客户端没有任何通信)后关闭通道?关闭通道后又如何再次建立连接?
②客户端宕机时,我们无需考虑,下次重启客户端之后就可以与服务器建立连接,但服务器宕机时,客户端如何与服务器端通信?
Netty重新连接机制,判断连接是否可用,不可用就重新连接
心跳检测
我们使用Socket通信一般经常会处理多个服务器之间的心跳检测,一般来讲我们去维护服务器集群,肯定要有一台或多台服务器主机(Master),然后还应该有N台(Slave),那么我们的主机肯定要时时刻刻知道自己下面的从服务器的各方面情况,然后进行实时监控的功能。这个在分布式架构里交做心跳检测或者心跳监控。最佳处理方案是使用一些通信框架进行实现,Netty就可以做这样的事。
11.Spring源码的理解
容器创建时做了什么?
getBean()时又做了什么?
12. 什么是内存泄漏, 内存溢出, 如何解决?
13. Spring的源码有读过吗?
14.讲一下Dubbo consumer接口调用流程
15.Java序列化实现原理
16.常用的java 设计模式有哪些.
17.sleep 和 wait 的区别, 应用场景
18.说一下脏读, 不可重复读, 幻读
19.事务的传播属性
20.有没有用过定时任务,批量任务是如何分配
21.JVM调优, JVM分配的内存多少合适?
1、Sun官方建议年轻代的大小为整个堆的3/8左右, 所以按照上述设置的方式,基本符合Sun的建议。
2、堆大小=年轻代大小+年老代大小, 即xmx=xmn+老年代大小 。 Permsize不影响堆大小。
3、为什么要按照上面的来进行设置呢? 没有具体的说明,但应该是根据多种调优之后得出的一个结论。