蚂蚁金服面试题及答案-总结

前言
相信有人在网上看到过一样的题,这里我也是从某篇公众号把题抄下来,答案都是笔者自己在网上搜的,适合即时回答,所以很多知识没有引入太深。

题目出自:http://www.54tianzhisheng.cn/2018/07/30/alipay01/, http://www.54tianzhisheng.cn/2018/07/31/alipay02/

一面
1、自我介绍、自己做的项目和技术领域

开放题

2、项目中的监控:那个监控指标常见的有哪些?

答:CPU、内存、IO 等等。建议下载个nmon工具,里面有各个指标。

数据库:Mysql(缓存命中、索引、单条SQL性能、数据库线程数、数据池连接数)

中间件:1.消息2、负载均衡3、缓存(包括线程数、连接数、日志)。

网络: 吞吐量、吞吐率

应用: jvm内存、日志、Full GC频率

3、微服务涉及到的技术以及需要注意的问题有哪些?

4、注册中心你了解了哪些?

答:Consul 、Eureka、ZooKeeper

5、consul 的可靠性你了解吗?

6、consul 的机制你有没有具体深入过?有没有和其他的注册中心对比过?

7、项目用 Spring 比较多,有没有了解 Spring 的原理?AOP 和 IOC 的原理

答:(1). IoC(Inversion of Control)是指容器控制程序对象之间的关系,而不是传统实现中,由程序代码直接操控。控制权由应用代码中转到了外部容器,控制权的转移是所谓反转。 对于Spring而言,就是由Spring来控制对象的生命周期和对象之间的关系;IoC还有另外一个名字——“依赖注入(Dependency Injection)”。从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,即由容器动态地将某种依赖关系注入到组件之中。  

(2). 在Spring的工作方式中,所有的类都会在spring容器中登记,告诉spring这是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。

(3). 在系统运行中,动态的向某个对象提供它所需要的其他对象。  

(4). 依赖注入的思想是通过反射机制实现的,在实例化一个类时,它通过反射调用类中set方法将事先保存在HashMap中的类属性注入到类中。 总而言之,在传统的对象创建方式中,通常由调用者来创建被调用者的实例,而在Spring中创建被调用者的工作由Spring来完成,然后注入调用者,即所谓的依赖注入or控制反转。 注入方式有两种:依赖注入和设置注入; IoC的优点:降低了组件之间的耦合,降低了业务对象之间替换的复杂性,使之能够灵活的管理对象。

AOP(Aspect Oriented Programming)

(1). AOP面向方面编程基于IoC,是对OOP的有益补充;

(2). AOP利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了 多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的 逻辑或责任封装起来,比如日志记录,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

(3). AOP代表的是一个横向的关 系,将“对象”比作一个空心的圆柱体,其中封装的是对象的属性和行为;则面向方面编程的方法,就是将这个圆柱体以切面形式剖开,选择性的提供业务逻辑。而 剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹,但完成了效果。

(4). 实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。

(5). Spring实现AOP:JDK动态代理和CGLIB代理 JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理;其核心的两个类是InvocationHandler和Proxy。 CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强;需要引入包asm.jar和cglib.jar。     使用AspectJ注入式切面和@AspectJ注解驱动的切面实际上底层也是通过动态代理实现的。

(6). AOP使用场景:                     

Authentication 权限检查        

Caching 缓存        

Context passing 内容传递        

Error handling 错误处理        

Lazy loading 延迟加载        

Debugging  调试      

logging, tracing, profiling and monitoring 日志记录,跟踪,优化,校准        

Performance optimization 性能优化,效率检查        

Persistence  持久化        

Resource pooling 资源池        

Synchronization 同步        

Transactions 事务管理    

另外Filter的实现和struts2的拦截器的实现都是AOP思想的体现。

8、Spring Boot除了自动配置,相比传统的 Spring 有什么其他的区别?

为Spring 生态系统的开发提供一种更简洁的方式,提供了很多非功能性特性,例如:嵌入式 Server,Security,统计,健康检查,外部配置等等,主要体现在以下几点:

1.Spring Boot可以建立独立的Spring应用程序;

2.内嵌了如Tomcat,Jetty和Undertow这样的容器,也就是说可以直接跑起来,用不着再做部署工作了;

3.无需再像Spring那样搞一堆繁琐的xml文件的配置;

4.可以自动配置Spring。SpringBoot将原有的XML配置改为Java配置,将bean注入改为使用注解注入的方式(@Autowire),并将多个xml、properties配置浓缩在一个appliaction.yml配置文件中。

5.提供了一些现有的功能,如量度工具,表单数据验证以及一些外部配置这样的一些第三方功能;

6.整合常用依赖(开发库,例如spring-webmvc、jackson-json、validation-api和tomcat等),提供的POM可以简化Maven的配置。当我们引入核心依赖时,SpringBoot会自引入其他依赖。

9、Spring Cloud 有了解多少?

10、Spring Bean 的生命周期

一个Bean从创建到销毁,如果是用BeanFactory来生成,管理Bean的话

Spring上下文中的Bean也类似,如下

    1、实例化一个Bean--也就是我们常说的new;

    2、按照Spring上下文对实例化的Bean进行配置--也就是IOC注入;

    3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值

    4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);

    5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法);

    6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;

    7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。

    8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法、;

    注:以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton,这里我们不做赘述。

    9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;

    10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

另外我们这里描述的是应用Spring上下文Bean的生命周期,如果应用Spring的工厂也就是BeanFactory的话去掉第5步就Ok了

11、HashMap 和 hashTable 区别?

区别:Hashtable是线程安全的,效率比较低

Hashtable既不支持Null key也不支持Null value。Hashtable的put()方法的注释中有说明

Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。

HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍

Hashtable在计算元素的位置时需要进行一次除法运算,而除法运算是比较耗时的

HashMap为了提高计算效率,将哈希表的大小固定为了2的幂,这样在取模预算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。

HashMap是继承自AbstractMap类,而HashTable是继承自Dictionary类。不过它们都实现了同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口

12、Object 的 hashcode 方法重写了,equals 方法要不要改?

不需要,Ojbect类中有两个方法equals、hashCode,这两个方法都是用来比较两个对象是否相等的,如果两个对象相等(equal),那么必须拥有相同 的哈希码(hash code)

即使两个对象有相同的哈希值(hash code),他们不一定相等

重写equals()方法就必须重写hashCode(),但重写hashcode方法不一定要重写equals方法

13、Hashmap 线程不安全的出现场景

用ConcurrentHashMap 线程安全

多线程处理时hashmap线程不安全

首先hashmap里这个size没有用volatile关键字修饰,代表这不是一个内存可见的变量,线程操作数据的时候一般是从主存拷贝一个变量副本进行操作,操作完成过后在把size的值写回到主存size的

线程不安全问题应该属于并发问题之一的,属于相对高级的问题了。这个时候的问题已经不仅仅局限于代码层面了,很多时候需要结合JVM一起分析了

14、线上服务 CPU 很高该怎么做?有哪些措施可以找到问题

定位出现问题的堆栈信息排查具体问题

1、top命令:Linux命令。可以查看实时的CPU使用情况。也可以查看最近一段时间的CPU使用情况。

2、ps命令: Linux命令。强大的进程状态监控命令。可以查看进程以及进程中线程的当前CPU使用情况。属于当前状态的采样数据。

3、jstack:  Java提供的命令。可以查看某个进程的当前线程栈运行情况。根据这个命令的输出可以定位某个进程的所有线程的当前运行状态、运行代码,以及是否死锁等等。

4、pstack:Linux命令。可以查看某个进程的当前线程栈运行情况

 15、JDK 中有哪几个线程池?顺带把线程池讲了个遍

JUC提供了调度器对象Executors来创建线程池,可创建的线程池有四种

1、newFixedThreadPool创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。

2、newCachedThreadPool创建一个可缓存的线程池。这种类型的线程池特点是:

1).工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。

2).如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。

3、newSingleThreadExecutor创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它,保证顺序执行(我觉得这点是它的特色)。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的 。

4、newScheduleThreadPool创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。(这种线程池原理暂还没完全了解透彻)

16、SQL 优化的常见方法有哪些

查询条件减少使用函数,避免全表扫描
减少不必要的表连接
有些数据操作的业务逻辑可以放到应用层进行实现
可以使用with as
尽量避免使用游标,因为游标的效率较差
不要把SQL语句写得太复杂
不能循环执行查询
用 exists 代替 in 
表关联关系不要太纠结
查询多用索引列取查,用charindex或者like[0-9]来代替%%
inner关联的表可以先查出来,再去关联leftjoin的表
可以进行表关联数据拆分,即先查出核心数据,再通过核心数据查其他数据,这样会快得多
参考SQL执行顺序进行优化
表关联时取别名,也能提高效率
使用视图,给视图建立索引进行优化
使用数据仓库的形式,建立单独的表存储数据,根据时间戳定期更新数据。将多表关联的数据集中抽取存入一张表中,查询时单表查询,提高了查询效率
对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引
应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:    
select id from t where num is null     
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:   
select id from t where num=0       
   19.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描

17、SQL 索引的顺序,字段的顺序

18、查看 SQL 是不是使用了索引?(有什么工具)

在select语句前加上EXPLAIN即可

19、TCP 和 UDP 的区别?TCP 数据传输过程中怎么做到可靠的?

UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种

1)为了保证数据包的可靠传递,发送方必须把已发送的数据包保留在缓冲区; 
(2)并为每个已发送的数据包启动一个超时定时器; 
(3)如在定时器超时之前收到了对方发来的应答信息(可能是对本包的应答,也可以是对本包后续包的应答),则释放该数据包占用的缓冲区; 
(4)否则,重传该数据包,直到收到应答或重传次数超过规定的最大次数为止。
(5)接收方收到数据包后,先进行CRC校验,如果正确则把数据交给上层协议,然后给发送方发送一个累计应答包,表明该数据已收到,如果接收方正好也有数据要发给发送方,应答包也可方在数据包中捎带过去。
20、说下你知道的排序算法吧

常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等

21、查找一个数组的中位数?

通过二分查找法来找中位数

基本思想是:假设ar1[i]是合并后的中位数,那么ar1[i]大于ar1[]中前i-1个数,且大于ar2[]中前j=n-i-1个数。通过ar1[i]和ar2[j]、ar2[j+1]两个数的比较,在ar1[i]的左边或者ar1[i]右边继续进行二分查找。对于两个数组 ar1[] 和ar2[], 先在 ar1[] 中做二分查找。如果在ar1[]中没找到中位数, 继续在ar2[]中查找。

算法流程:
1) 得到数组ar1[]最中间的数,假设下标为i.
2) 计算对应在数组ar2[]的下标j,j = n-i-1 
3) 如果 ar1[i] >= ar2[j] and ar1[i] <= ar2[j+1],那么 ar1[i] 和 ar2[j] 就是两个中间元素,返回ar2[j] 和 ar1[i] 的平均值
4) 如果 ar1[i] 大于 ar2[j] 和 ar2[j+1] 那么在ar1[i]的左部分做二分查找(i.e., arr[left ... i-1])
5) 如果 ar1[i] 小于 ar2[j] 和 ar2[j+1] 那么在ar1[i]的右部分做二分查找(i.e., arr[i+1....right])
6) 如果到达数组ar1[]的边界(left or right),则在数组ar2[]中做二分查找

时间复杂度:O(logn)。

二面
22、你有什么问题想问我的吗?
1、自我介绍、工作经历、技术栈

2、项目中你学到了什么技术?

3、微服务划分的粒度?

4、微服务的高可用怎么保证的?

负载均衡与反向代理,隔离,限流,降级,超时与重试,回滚,压力测试与应急预案

5、常用的负载均衡,该怎么用,你能说下吗?

1、http重定向

当http代理(比如浏览器)向web服务器请求某个URL后,web服务器可以通过http响应头信息中的Location标记来返回一个新的URL。这意味着HTTP代理需要继续请求这个新的URL,完成自动跳转。

2、DNS负载均衡

DNS 负责提供域名解析服务,当访问某个站点时,实际上首先需要通过该站点域名的DNS服务器来获取域名指向的IP地址,在这一过程中,DNS服务器完成了域名到IP地址的映射,同样,这样映射也可以是一对多的,这时候,DNS服务器便充当了负载均衡调度器,它就像http重定向转换策略一样,将用户的请求分散到多台服务器上,但是它的实现机制完全不同。

3、反向代理负载均衡

这个肯定大家都有所接触,因为几乎所有主流的Web服务器都热衷于支持基于反向代理的负载均衡。它的核心工作就是转发HTTP请求。

相比前面的HTTP重定向和DNS解析,反向代理的调度器扮演的是用户和实际服务器中间人的角色:

     1、任何对于实际服务器的HTTP请求都必须经过调度器

     2、调度器必须等待实际服务器的HTTP响应,并将它反馈给用户(前两种方式不需要经过调度反馈,是实际服务器直接发送给用户)

4、IP负载均衡(LVS-NAT)

因为反向代理服务器工作在HTTP层,其本身的开销就已经严重制约了可扩展性,从而也限制了它的性能极限。那能否在HTTP层面以下实现负载均衡呢?

NAT服务器:它工作在传输层,它可以修改发送来的IP数据包,将数据包的目标地址修改为实际服务器地址

5、直接路由(LVS-DR)

NAT是工作在网络分层模型的传输层(第四层),而直接路由是工作在数据链路层(第二层),貌似更屌些。它通过修改数据包的目标MAC地址(没有修改目标IP),将数据包转发到实际服务器上,不同的是,实际服务器的响应数据包将直接发送给客户羰,而不经过调度器

6、IP隧道(LVS-TUN)

基于IP隧道的请求转发机制:将调度器收到的IP数据包封装在一个新的IP数据包中,转交给实际服务器,然后实际服务器的响应数据包可以直接到达用户端。目前Linux大多支持,可以用LVS来实现,称为LVS-TUN,与LVS-DR不同的是,实际服务器可以和调度器不在同一个WANt网段,调度器通过 IP隧道技术来转发请求到实际服务器,所以实际服务器也必须拥有合法的IP地址。

总体来说,LVS-DR和LVS-TUN都适合响应和请求不对称的Web服务器,如何从它们中做出选择,取决于你的网络部署需要,因为LVS-TUN可以将实际服务器根据需要部署在不同的地域,并且根据就近访问的原则来转移请求,所以有类似这种需求的,就应该选择LVS-TUN。

6、网关能够为后端服务带来哪些好处?

后端服务器可以专心处理业务请求,节省了大量连接管理的开销

7、Spring Bean 的生命周期

8、xml 中配置的 init、destroy 方法怎么可以做到调用具体的方法?

9、反射的机制

大家都知道,要让Java程序能够运行,那么就得让Java类要被Java虚拟机加载。Java类如果不被Java虚拟机加载,是不能正常运行的。现在我们运行的所有的程序都是在编译期的时候就已经知道了你所需要的那个类的已经被加载了。

Java的反射机制是在编译并不确定是哪个类被加载了,而是在程序运行的时候才加载、探知、自审。使用在编译期并不知道的类。这样的特点就是反射

反射机制通过void setAccessible(boolean flag)方法可以得到一个类的private的方法和属性,使用这些private的方法和属性

10、Object 类中的方法

1,构造函数 
2,hashCode和equale函数用来判断对象是否相同, 
3,wait(),wait(long),wait(long,int),notify(),notifyAll() 
4,toString()和getClass, 
5,clone() 
6,finalize()用于在垃圾回收

11、hashcode 和 equals 方法常用地方

12、对象比较是否相同

equals通常用来比较两个对象的内容是否相等,==用来比较两个对象的地址是否相等

13、hashmap put 方法存放的时候怎么判断是否是重复的

先比较key的hashCode,再比较相等或equals的,所以重写hashCode()和equals()方法即可实现添加重复元素。

14、Object toString 方法常用的地方,为什么要重写该方法

常用在对象模型类

因为假如User是一个用户的对象,如果User.toString();结果是不正常的,因为User对象中可能有多个属性,如年龄,姓名等,这个toString后无法知道具体的是那个属性转换为字符串

15、Set 和 List 区别?

Set(集):集合中的对象不按特定方式排序,并且没有重复对象。它的有些实现类能对集合中的对象按特定方式排序。
List(列表):集合中的对象按索引位置排序,可以有重复对象,允许按照对象在集合中的索引位置检索对象。

16、ArrayList 和 LinkedList 区别

 ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构

ArrayList 继承AbstractList
LinkedList 继承AbstractSequentialList
ArrayList 采用的是数组形式来保存对象的,这种方式将对象放在连续的位置中,所以最大的缺点就是插入删除时非常麻烦

LinkedList 采用的将对象存放在独立的空间中,而且在每个空间中还保存下一个链接的索引 但是缺点就是查找非常麻烦 要丛第一个索引开始

17、如果存取相同的数据,ArrayList 和 LinkedList 谁占用空间更大?

对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针

对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据,若要从数组中删除或插入某一个对象,需要移动后段的数组元素,从而会重新调整索引顺序,调整索引顺序会消耗一定的时间,相反,LinkedList是使用链表实现的,若要从链表中删除或插入某一个对象,只需要改变前后对象的引用即可

18、Set 存的顺序是有序的吗?

无序

Set是Map的一个马甲,主要逻辑都交给Map实现

19、常见 Set 的实现有哪些?

HashSet

LinkedHashSet

TreeSet

20、TreeSet 对存入对数据有什么要求呢?

TreeSet集合是用来对象元素进行排序的,同样他也可以保证元素的唯一

21、HashSet 的底层实现呢?

22、TreeSet 底层源码有看过吗?

TreeSet的底层实现是TreeMap

public TreeSet(Comparator<? super E> comparator) {

        this(new TreeMap<>(comparator));

    }

23、HashSet 是不是线程安全的?为什么不是线程安全的?

说白了,HashSet就是限制了功能的HashMap,所以了解HashMap的实现原理

24、Java 中有哪些线程安全的 Map?

Concurrenthashmap

25、Concurrenthashmap 是怎么做到线程安全的?

ConcurrentHashMap的大部分操作和HashMap是相同的,例如初始化,扩容和链表向红黑树的转变等。但是,在ConcurrentHashMap中,大量使用了U.compareAndSwapXXX

的方法,这个方法是利用一个CAS算法实现无锁化的修改值的操作,他可以大大降低锁代理的性能消耗。这个算法的基本思想就是不断地去比较当前内存中的变量值与你指定的

一个变量值是否相等,如果相等,则接受你指定的修改的值,否则拒绝你的操作。因为当前线程中的值已经不是最新的值,你的修改很可能会覆盖掉其他线程修改的结果。这一

点与乐观锁,SVN的思想是比较类似的。

同时,在ConcurrentHashMap中还定义了三个原子操作,用于对指定位置的节点进行操作。这三种原子操作被广泛的使用在ConcurrentHashMap的get和put等方法中,

正是这些原子操作保证了ConcurrentHashMap的线程安全。

在ConcurrentHashMap没有出现以前,jdk使用hashtable来实现线程安全,但是hashtable是将整个hash表锁住,所以效率很低下。

ConcurrentHashMap将数据分别放到多个Segment中,默认16个,每一个Segment中又包含了多个HashEntry列表数组,

对于一个key,需要经过三次hash操作,才能最终定位这个元素的位置,这三次hash分别为:

对于一个key,先进行一次hash操作,得到hash值h1,也即h1 = hash1(key);
将得到的h1的高几位进行第二次hash,得到hash值h2,也即h2 = hash2(h1高几位),通过h2能够确定该元素的放在哪个Segment;
将得到的h1进行第三次hash,得到hash值h3,也即h3 = hash3(h1),通过h3能够确定该元素放置在哪个HashEntry。
每一个Segment都拥有一个锁,当进行写操作时,只需要锁定一个Segment,而其它Segment中的数据是可以访问的。

26、HashTable 你了解过吗?

Hashtable既不支持Null key也不支持Null value。Hashtable的put()方法的注释中有说明

Hashtable是线程安全的,

Hashtable是线程安全的,它的每个方法中都加入了Synchronize方法,效率比较低

Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。

Hashtable在计算元素的位置时需要进行一次除法运算,而除法运算是比较耗时的。

27、如何保证线程安全问题?

28、synchronized、lock

synchronized是java中的一个关键字,也就是说是Java语言内置的特性

如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:

  1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;

  2)线程执行发生异常,此时JVM会让线程自动释放锁

那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,试想一下,这多么影响程序执行效率。

  因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到

再举个例子:当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象。

  但是采用synchronized关键字来实现同步的话,就会导致一个问题:

  如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。

  因此就需要一种机制来使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到。

另外,通过Lock可以知道线程有没有成功获取到锁。这个是synchronized无法办到的

29、volatile 的原子性问题?为什么 i++ 这种不支持原子性?从计算机原理的设计来讲下不能保证原子性的原因

30、happens before 原理

31、cas 操作

java.util.concurrent包中借助CAS实现了区别于synchronized同步锁的一种乐观锁

cas是比较并交换算法

 CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做

JDK提供了AtomicReference类来保证引用对象之间的原子性,就可以把多个变量放在一个对象里来进行CAS操作。

32、lock 和 synchronized 的区别?

    1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

  2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

  3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

  4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

  5)Lock可以提高多个线程进行读操作的效率。

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

 

类别

synchronized

Lock

存在层次

Java的关键字,在jvm层面上

是一个类

锁的释放

1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁

在finally中必须释放锁,不然容易造成线程死锁

锁的获取

假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待

分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待

锁状态

无法判断

可以判断

锁类型

可重入 不可中断 非公平

可重入 可判断 可公平(两者皆可)

性能

少量同步

大量同步

33、公平锁和非公平锁

公平和非公平锁的队列都基于锁内部维护的一个双向链表,表结点Node的值就是每一个请求当前锁的线程。公平锁则在于每次都是依次从队首取值

非公平锁在等待锁的过程中, 如果有任意新的线程妄图获取锁,都是有很大的几率直接获取到锁的

(在ReentrantLock中很明显可以看到其中同步包括两种,分别是公平的FairSync和非公平的NonfairSync。公平锁的作用就是严格按照线程启动的顺序来执行的,不允许其他线程插队执行的;而非公平锁是允许插队的。

默认情况下ReentrantLock是通过非公平锁来进行同步的,包括synchronized关键字都是如此,因为这样性能会更好。因为从线程进入了RUNNABLE状态,可以执行开始,到实际线程执行是要比较久的时间的。而且,在一个锁释放之后,其他的线程会需要重新来获取锁。其中经历了持有锁的线程释放锁,其他线程从挂起恢复到RUNNABLE状态,其他线程请求锁,获得锁,线程执行,这一系列步骤。如果这个时候,存在一个线程直接请求锁,可能就避开挂起到恢复RUNNABLE状态的这段消耗,所以性能更优化)

34、Java 读写锁

35、读写锁设计主要解决什么问题?

多线程,

读操作可以共享,写操作是排他的,读可以有多个在读,写只有唯一个在写,同时写的时候不允许读

解决了读和读可以同时进行,读和写不能同时进行,写和写不能同时进行

36、你项目除了写 Java 代码,还有前端代码,那你知道前端有哪些框架吗?

Vue layer react element

37、MySQL 分页查询语句

LIMIT [offset,] rows

offset指定要返回的第一行的偏移量,rows第二个指定返回行的最大数目

38、MySQL 事务特性和隔离级别

事务的基本要素(ACID)
1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。

   2、一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。

   3、隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。

   4、持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

二、事务的并发问题

 

1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据

  2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。

  3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

 

MySQL事务隔离级别

事务隔离级别

脏读

不可重复读

幻读

读未提交(read-uncommitted)

不可重复读(read-committed)

可重复读(repeatable-read)

串行化(serializable)

 

39、不可重复读会出现在什么场景?

40、sql having 的使用场景

如果需要对组函数的结果作为条件,那么不能使用where子句,必须使用having子句

41、前端浏览器地址的一个 http 请求到后端整个流程是怎么样?

能够说下吗?

域名解析 --> 发起TCP的3次握手 --> 建立TCP连接后发起http请求 -->服务器响应http请求,浏览器得到html代码 -->浏览器解析html代码,并请求html代码中的资源(如js、css、图片等) --> 浏览器对页面进行渲染呈现给用户

42、http 默认端口,https 默认端口

HTTP协议代理服务器常用端口号:80/8080/3128/8081/9080
HTTPS服务器,默认的端口号为443/tcp 443/udp

43、DNS 你知道是干嘛的吗?

DNS是指:域名服务器(Domain Name Server)。在Internet上域名与IP地址之间是一一对应的,域名虽然便于人们记忆,但机器之间只能互相认识IP地址,它们之间的转换工作称为域名解析,域名解析需要由专门的域名解析服务器来完成,DNS就是进行域名解析的服务器

44、你们开发用的 ide 是啥?你能说下 idea 的常用几个快捷键吧?

45、代码版本管理你们用的是啥?

46、git rebase 和 merge 有什么区别?

47、你们公司加班多吗?


 

注:有些问题待补充。


原文:https://blog.csdn.net/weixin_38003389/article/details/83747310 
版权声明:本文为博主原创文章,转载请附上博文链接!

发布了11 篇原创文章 · 获赞 8 · 访问量 2万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览