query

sleep() 和 wait() 有什么区别

  sleep()不释放同步锁
  wait()释放同步锁


equals和hashCode的关系

  如果两个对象相等(equal),它们的hashcode一定相同;
  如果两个对象有相同的hashcode,它们不一定相等(equal);
  之所以这样设计是为了在Map中更快的查找到对象(相对于线性搜索);
  一般Map都设计成数组+链表的结构,使用hashcode去查找对象需要两个步骤,首先使用hashcode定位数组下标索引,然后遍历该数组元素对应的链表,找到equals的元素;Object默认的hashcode实现对于不同的对象会返回不同的值,因此,不同的对象(即使同一个类型)有着不同的hashcode;


final 修饰的类和方法有什么特点

  final类不能被继承,没有子类,final类中的方法默认是final的
  final方法不能被子类的方法覆盖,但可以被继承。


final的属性可以为null吗

  可以


如何clone一个对象

  可以使用Object 类中的clone()方法,clone()方法可以用来完成对象的浅克隆。


Object上clone方法的访问修饰符是什么

  native
  说明这个方法的实现不是在java中,而是由C/C++实现。

如果没有实现Cloneable,只是类里面其他方法需要调用clone,可以直接调吗

  Cloneable接口是不包含任何方法的!其实这个接口仅仅是一个标志,而且这个标志也仅仅是针对 Object类中clone()方法的,如果clone类没有实现Cloneable接口,并调用了Object的clone()方法(也就是调用了 super.Clone()方法),那么Object的clone()方法就会抛出CloneNotSupportedException异常。

java是怎么实现多态

  java使用了后期绑定的概念,在方法区查找。

在传统的MVC模式中,他们分别扮演什么样的角色

  模型(model)-视图(view)-控制器(controller)


spring的ioc和aop分别是什么意思
  控制反转(工厂模式、java反射机制和jdk的操作XML的DOM解析方式)
  面向切面(代理模式)


Annotation保留策略有哪几个,Annotation可以继承吗,该怎么做

  SOURCE、CLASS、RUNTIME

  @Inherited

 

transient有什么用

  1、transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。
  2、被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。
  3、一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。也可以认为在将持久化的对象反序列化后,被transient修饰的变量将按照普通类成员变量一样被初始化。

java反射,性能如何优化

  1 JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查

  2 用缓存,比如redis, 这个方法提高效率是显而易见的。将反射得到元数据保存起来,使用时,只需从内存中调用即可。

  3 利用一些高性能的反射库,如ReflectASM

 

Integer.MaxValue+1是多少?

  负数的源码是补码取反+1,刚好是Integer.MIN_VALUE

Java threadlocal使用时要注意什么

  需要注意的是,每一个线程都只是使用ThreadLocal标注变量的副本进行计

 

JDBC resultset fetchsize 和maxrows有什么用

  setMaxRows方法的话是取得最大行,最大以后的数据会被丢掉。设置这个参数虽然可以避免报内存错误,不过在很多场合没法使用,因为查询的结果肯定想完整的抽取出来的情况很多。这个方法和limit类似。

  setFetchSize方法的话是JDBC查询和从结果集里面每次取设置行数,循环去取,直到取完。这个方法很常用的方法。默认是size是0.也就是默认是一次性把结果集的数据全部取出来,这样就容易造成内存不足。这个方法好像在自动提交模式下无效,需设置autocommit为false。

 

WeakHashmap
  以弱键实现的基于哈希表的 Map。在 WeakHashMap 中,当某个键不再正常使用时,将自动移除其条目。

 

LinkedList是双向循环链表吗
  不是双向循环链表是双向链表

 

Filter如何在response 返回后做事情?

  返回值输出代理类:这个类主要是为了把Response里面的返回值获取到,因为直接Response没有提供直接拿到返回值的方法。所以要通过代理来取得返回值。


JTA:Spring 是怎么实现 JTA Transaction manager的

 

Synchronized和lock之间的区别

  来源:lock是一个接口,而synchronized是java的一个关键字,synchronized是内置的语言实现;

  异常是否释放锁:synchronized在发生异常时候会自动释放占有的锁,因此不会出现死锁;而lock发生异常时候,不会主动释放占有的锁,必须手动unlock来释放锁,可能引起死锁的发生。(所以最好将同步代码块用try catch包起来,finally中写入unlock,避免死锁的发生。)

  是否响应中断:lock等待锁过程中可以用interrupt来中断等待,而synchronized只能等待锁的释放,不能响应中断;

  是否知道获取锁:Lock可以通过trylock来知道有没有获取锁,而synchronized不能;

  Lock可以提高多个线程进行读操作的效率。(可以通过readwritelock实现读写分离)

  在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

  synchronized使用Object对象本身的wait 、notify、notifyAll调度机制,而Lock可以使用Condition进行线程之间的调度

CAS

  CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该 位置的值。

  ABA问题。因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A

 

redission是怎么实现分布式锁的

  线程1 进来获得锁后,线程一切正常并没有宕机,但它的业务逻辑需要执行2秒,这就会有个问题,在线程1执行1秒后,这个锁就自动过期了,那么这个时候 线程2 进来了。所以这个时候看门狗就出现了,它的作用就是 线程1业务还没有执行完,时间就过了,线程1 还想持有锁的话,就会启动一个watch dog后台线程,不断的延长锁key的生存时间。

 

redis的数据结构

  简单动态字符串

    可能会较为主观的认为 Redis 中的字符串就是采用了C语言中的传统字符串表示,但其实不然,Redis 没有直接使用C语言传统的字符串表示,而是自己构建了一种名为简单动态字符串(simple dynamic string SDS)的抽象类型,并将SDS用作Redis 的默认字符串

  链表

    链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活地调整链表的长度。

    链表在Redis 中的应用非常广泛,比如列表键的底层实现之一就是链表。当一个列表键包含了数量较多的元素,又或者列表中包含的元素都是比较长的字符串时,Redis 就会使用链表作为列表键的底层实现。

  字典 

    字典,又称为符号表(symbol table)、关联数组(associative array)或映射(map),是一种用于保存键值对的抽象数据结构。 

    在字典中,一个键(key)可以和一个值(value)进行关联,字典中的每个键都是独一无二的。在C语言中,并没有这种数据结构,但是Redis 中构建了自己的字典实现。

  跳表

    跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。跳跃表是一种随机化的数据,跳跃表以有序的方式在层次化的链表中保存元素,效率和平衡树媲美 ——查找、删除、添加等操作都可以在对数期望时间下完成,并且比起平衡    树来说,跳跃表的实现要简单直观得多。Redis 只在两个地方用到了跳跃表,一个是实现有序集合键,另外一个是在集群节点中用作内部数据结构。

  整数集合

    整数集合是集合建的底层实现之一,当一个集合中只包含整数,且这个集合中的元素数量不多时,redis就会使用整数集合intset作为集合的底层实现。

  压缩列表

    当一个列表键只包含少量列表项, 并且每个列表项要么就是小整数值, 要么就是长度比较短的字符串, 那么 Redis 就会使用压缩列表来做列表键的底层实现。

  对象

    Redis 并没有直接使用这些数据结构来实现键值对数据库, 而是基于这些数据结构创建了一个对象系统, 这个系统包含字符串对象、列表对象、哈希对象、集合对象和有序集合对象这五种类型的对象, 每种对象都用到了至少一种我们前面所介绍的数据结构。通过这五种不同类型的对象, Redis 可以在执行命令之前, 根据对象的类型来判断一个对象是否可以执行给定的命令。 使用对象的另一个好处是, 我们可以针对不同的使用场景, 为对象设置多种不同的数据结构实现, 从而优化对象在不同场景下的使用效率。除此之外, Redis 的对象系统还实现了基于引用计数技术的内存回收机制: 当程序不再使用某个对象的时候, 这个对象所占用的内存就会被自动释放; 另外, Redis 还通过引用计数技术实现了对象共享机制, 这一机制可以在适当的条件下, 通过让多个数据库键共享同一个对象来节约内存。最后, Redis 的对象带有访问时间记录信息, 该信息可以用于计算数据库键的空转时长, 在服务器启用了 maxmemory 功能的情况下, 空转时长较大的那些键可能会优先被服务器删除。

 

RabbitMQ如何保证消息的可靠性

  1 开启事务(不推荐)

  2 开启confirm(推荐)

  3 开启RabbitMQ持久化(交换机、队列、消息)

  4 关闭RabbitMQ自动ack(改成手动)

 

垃圾回收算法

  标记-清除算法

  复制算法

  标记-整理算法

  分代收集算法

 

mysql索引

  mysql索引的数据结构就是用到的B+树

  二叉树:如果数据是单边增长的情况 那么出现的就是和链表一样的数据结构了,树高度大

  红黑树:在二叉树的基础上多了树平衡,也叫二叉平衡树,不像二叉树那样极端的情况会往一个方向发展。

  B树:在红黑树的基础上,每个节点可以存放多个数据

  B+树:B树的变种,非叶子节点是会重复

 

mysql的隔离级别

  Read Uncommitted(读取未提交内容)

    在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

  Read Committed(读取提交内容)

    这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

  Repeatable Read(可重读)

    这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。

    InnoDB 里面每个事务有一个唯一的事务 ID,叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的。对于数据库的每行记录,都会有三个隐藏字段:db_trx_id (事务 id)、db_roll_pt (回滚指针)、delete_flag(删除标记)。

  Serializable(可串行化)

    这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

 

脏读

  读取到了未提交的数据(如果事务这时候回滚了,那么第二个事务就读到了脏数据)

 

不可重复读

  同一个事务中,对于同一数据,执行完全相同的select语句时可能看到不一样的结果。

 

幻读

  幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。

 

可重复读的底层实现原理

  使用的的一种叫MVCC的控制方式 ,即Mutil-Version Concurrency Control,多版本并发控制,类似于乐观锁的一种实现方式 

 

MVCC   

  InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号(system version number)。每开始一个新的事务,系统版本号都会自        动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。下面看一下在 REPEATABLE READ隔离级别下,MVCC具体是如何操作的。

  SELECT
    InnoDB会根据以下两个条件检查每行记录:
    a. InnoDB只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小.于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。
    b.行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除。
    只有符合上述两个条件的记录,才能返回作为查询结果。

  INSERT
    InnoDB为新插入的每一行保存当前系统版本号作为行版本号。

  DELETE
    InnoDB为删除的每一行保存当前系统版本号作为行删除标识。

  UPDATE
    InnoDB为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。

  保存这两个额外系统版本号,使大多数读操作都可以不用加锁。这样设计使得读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行。不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作。

  MVCC 只在 REPEATABLE READ和 READ COMMITTED两个隔离级别下工作。其他两个隔离级别都和MVCC不兼容,因为READ UNCOMMITTED总是读取最新的数据行,而不是符合当前事务版本的数据行。而SERIALIZABLE则会对所有读取的行都加锁。

 

SpringCloud 

  Eureka 服务发现框架
  Ribbon 进程内负载均衡器
  Open Feign 服务调用映射
  Hystrix 服务降级熔断器
  Zuul 微服务网关
  Config 微服务统一配置中心
  Bus 消息总线

 

注册中心怎么保证自己的高可用的

  将注册中心之间相互注册,信息共享。同时客户端也同时向每个注册中心注册。这样当一个注册中心down掉,并不会导致服务停止。

 

分布式事务解决方案

  2PC、3PC、AT、TCC、SAGA 和 XA 事务模式

 

2PC

  2PC(Two-phase commit protocol),中文叫二阶段提交。 二阶段提交是一种强一致性设计,2PC 引入一个事务协调者的角色来协调管理各参与者(也可称之为各本地资源)的提交和回滚,二阶段分别指的是准备(投票)和提交两个阶段。

  准备阶段协调者会给各参与者发送准备命令,你可以把准备命令理解成除了提交事务之外啥事都做完了。

  同步等待所有资源的响应之后就进入第二阶段即提交阶段(注意提交阶段不一定是提交事务,也可能是回滚事务)。

  假如在第一阶段所有参与者都返回准备成功,那么协调者则向所有参与者发送提交事务命令,然后等待所有事务都提交成功之后,返回事务执行成功。

 

3PC

  3PC 的出现是为了解决 2PC 的一些问题,相比于 2PC 它在参与者中也引入了超时机制,并且新增了一个阶段使得参与者可以利用这一个阶段统一各自的状态。

  3PC 包含了三个阶段,分别是准备阶段、预提交阶段和提交阶段,对应的英文就是:CanCommit、PreCommit 和 DoCommit

  看起来是把 2PC 的提交阶段变成了预提交阶段和提交阶段,但是 3PC 的准备阶段协调者只是询问参与者的自身状况,比如你现在还好吗?负载重不重?这类的。

  而预提交阶段就是和 2PC 的准备阶段一样,除了事务的提交该做的都做了。

  提交阶段和 2PC 的一样

 

TCC

  2PC 和 3PC 都是数据库层面的,而 TCC 是业务层面的分布式事务,就像我前面说的分布式事务不仅仅包括数据库的操作,还包括发送短信等,这时候 TCC 就派上用场了!

  TCC 指的是Try - Confirm - Cancel

    • Try 指的是预留,即资源的预留和锁定,注意是预留。
    • Confirm 指的是确认操作,这一步其实就是真正的执行了。
    • Cancel 指的是撤销操作,可以理解为把预留阶段的动作撤销了。

  其实从思想上看和 2PC 差不多,都是先试探性的执行,如果都可以那就真正的执行,如果不行就回滚。

  比如说一个事务要执行A、B、C三个操作,那么先对三个操作执行预留动作。如果都预留成功了那么就执行确认操作,如果有一个预留失败那就都执行撤销动作。

 

消息事务

  RocketMQ 就很好的支持了消息事务,让我们来看一下如何通过消息实现事务。

  第一步先给 Broker 发送事务消息即半消息,半消息不是说一半消息,而是这个消息对消费者来说不可见,然后发送成功后发送方再执行本地事务。

  再根据本地事务的结果向 Broker 发送 Commit 或者 RollBack 命令。

  并且 RocketMQ 的发送方会提供一个反查事务状态接口,如果一段时间内半消息没有收到任何操作请求,那么 Broker 会通过反查接口得知发送方事务是否执行成功,然后执行 Commit 或者 RollBack 命令。

  如果是 Commit 那么订阅方就能收到这条消息,然后再做对应的操作,做完了之后再消费这条消息即可。

  如果是 RollBack 那么订阅方收不到这条消息,等于事务就没执行过。

  可以看到通过 RocketMQ 还是比较容易实现的,RocketMQ 提供了事务消息的功能,我们只需要定义好事务反查接口即可。

 

posted on 2021-10-08 18:00  1zfang1  阅读(331)  评论(0编辑  收藏  举报

导航