java的重载和重写的区别是什么?
重写:出现在继承关系中,指的是子类重写父类的方法,方法名相同,参数列表相同,访问修饰符不能缩小父类的访问权限,返回值等于父类或者是父类的子类
重载:方法名一样,参数列表不一样,返回值可以不同
方法签名包含哪些部分?
方法名,参数列表,返回值(注意不包括访问修饰符)
如果方法的返回值不同,其他的都一样,可以形成重写或者重载吗?会有什么问题?
可能会形成重写
可读性降低
如果返回值类型不是父类的类型或者父类的子类型,那么就不是重写,不能形成有效的重写
HashMap的数据结构是什么,是线程安全的吗?
hashmap的数据结构是 数组 + 链表 + 红黑树,非线程安全的!
hashmap不是线程安全的原因是他的内部结构以及操作不是线程安全的
- 非同步操作:hashmap操作不是线程同步的,也就是说,多线程的环境下操作hashmap可能会导致数据不一致
- 非原子操作:hashmap的操作方法不是原子性的,涉及到多个步骤,多线程环境下可能会导致数据不一致的问题
- 数组扩容:hashmap的扩容涉及到重新计算hash值以及复制到新的数组,如果其他的线程对hashmap进行修改,会导致数据丢失以及异常
解决:
- 使用同步机制:可以使用线程安全的Map来实现,例如concurrentMap来代替,或者在使用hashmap的时候,使用synchronized同步锁或者其他锁来控制同一时刻只能由一个线程来访问hashmap
ConcurrentHashMap怎么实现线程安全的?
ConcurrentHashMap1.7 和 ConcurrentHashMap1.8实现线程安全的方式不同
1.7采用的数据结构是 Segment段 + HashEntry数组 + 链表,1.8采用的是 Node数组 + 链表 + 红黑树
1.7时,当我们要添加一个key时,首先计算key的hash值,得到Segment的下标,然后将这个Segment上锁,然后再通过key的hash值得到HashEntry数组的下标,后面的操作就和hashmap的一样了
1.8时,锁的粒度更加细,当我们要添加一个key时,计算hash值得到node数组的下标,如果计算的下标还没有node的时候,那么通过cas插入新的node,如果有node的话,则通过synchronized给这个node上锁(只锁住链表头结点或者树的头结点),这样别的线程就无法访问这个node节点以及后面的链表
segment段默认是16个,也就是说最大并发支持16个并发,并且一旦初始化之后就不能改变,每一个segment相当于一个hashmap
CAS原理
CAS就是比较和交换的意思,当我们要操作一个资源的时候,首先比较他是否和预期值相等,如果相等则交换更新为新的值,如果不想等则重试
这里面涉及到两个操作,一个是比较,一个是交换,假如他们是非原子性操作的话,那么多线程的环境下会出现数据不一致的问题,所以需要将这个两个操作封装的一个原子性指令,这就是CAS
CAS需要三个操作数,一个是旧的预期值,变量地址,新值
优点:
无锁并发,不会线程阻塞,原子性,线程安全
缺点:
ABA,如果一个变量从A变成B再变成A,CAS无法监测到这种变化,可能导致错误,使用版本号解决,更新变量的同时也更新版本号
自旋开销:如果更新失败的时候,会一直重复,比较交换这个过程,消耗性能
单变量限制:CAS适用于单个变量的更新,不适用于多个变量的复杂操作
b树和b+树的区别和优势
b+树的叶子节点存放数据,非叶子节点存放索引值
b树除了叶子节点存放数据,非叶子节点也存放数据和索引值
同等高度的情况下,b+树能够存放更多的索引值,索引命中的概率更加大,检索效率高
同等数据量的情况下,b树深度更高,io次数更多
另外,b+树叶子节点之间采用双向链表连接,使用于范围查询,而b树做不到这一点
什么情况下会导致(联合)索引失效
- 违反最左前缀匹配原则,按照左优先的方式进行匹配,(a,b,c),先按a匹配,再按b,再按c,如果where中没有a,那么索引失效
- 左右模糊匹配
- 索引列使用函数
- 索引列进行表达式计算
- 字符串和数字比较,字符串是索引列,那么会将字符串转换为数字,相当使用了cast函数
- where句子使用or,or左边是索引,右边不是索引,那么索引会失效
MVCC是如何实现并发事务管理的
mvcc即多版本并发控制,在可重复读隔离的级别下,快照读是通过ReadView + MVCC 来最大程度避免幻读的,在事务开启的时候,会创建一个ReadView,可以理解为一个快照,ReadView包含了事务id,活跃尚未提交的事务列表,活跃尚未提交的最小事务id,活跃尚未提交的最大的事务id,我们的记录中隐含着两个列,一个是事务id,一个是指针地址,当我们要去访问一个资源记录时,会读取这个记录的隐藏列中的事务id,看看这个事务id是不是尚未提交的事务的id,如果是,则根据指针顺着版本链找到事务开始前的记录,读取该记录,从而实现控制并发事务管理,这个就叫MVCC
事务加锁的过程
缓存雪崩、穿透、击穿
雪崩:大量热点key同时过期,导致请求穿过缓存全部打到数据库上,造成数据库压力剧增
穿透:缓存中某个热点key过期了,大量的请求访问这个热点key,导致请求全部打到数据库上
击穿:大量的请求进来,缓存中没有命中key,数据库中也不存在这个访问资源,导致数据库压力增大
