面试问题总结

记录一下面试时遇到的回答不好的问题

Java基础

Java的值传递和引用传递

Java里面只有值传递,对于基础类型是复制值,对于对象是拷贝引用的地址

IOC的底层原理是反射,反射对Java性能的影响:

反射真正损耗性能的地方在于getxxx操作,这些get方法之所以慢是因为根据方法名或字段名在获取method或field时类似于在class所在方法区做遍历匹配,拿到之后要么反射执行方法,要么反射field赋值;而如果是直接调用,则是通过将方法或属性的符号引用转变为直接引用找到方法或字段的所在,直接进行操作

Java集合

ConcurrentHashmap,读写和写写在一个节点上能并发吗?

可以,内部使用volatile来保证数据存储的可见性;利用CAS操作,在特定场景下进行无锁并发操作,内部的锁实际用的是syncronized;因为在jdk8中,syncronized已经得到性能的优化,并且对比再入锁可以减少内存消耗。

ArrayList的并发版本的集合CopyOnWriteArrayList,它的并发是怎么做的?

先拷贝一份,读操作读的是原来的数据,写操作再修改拷贝的,然后把拷贝的复制过去

AQS的队列为什么是双向队列

在队列同步器中,头节点是成功获取到同步状态的节点,而头节点的线程释放了同步状态后,将会唤醒其他后续节点,后继节点的线程被唤醒后需要检查自己的前驱节点是否是头节点,如果是则尝试获取同步状态。 所以为了能让后继节点获取到其前驱节点,同步队列便设置为双向链表,而等待队列没有这样的需求,就为单链表。

Java多线程

多线程并发中,多个线程直接给一个变量赋值或者赋值对象可以吗?

不可以,不管是对象还是引用,都是放在对应线程的CPU缓存中,需要保证一致性

读写锁中具体是怎么实现的?

state变量通过位运算,高16和低16位分别表示X锁和X锁

线程池里队列是怎么样的?

  • SynchronousQueue: 没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素。一般用于构造newCachedThreadPool
  • LinkedBlockingQueue:无界缓存等待队列。当前执行的线程数量达到corePoolSize的数量时,剩余的元素会在阻塞队列里等待。(所以在使用此阻塞队列时maximumPoolSizes就相当于无效了)
  • ArrayBlockingQueue:一个有界缓存等待队列,可以指定缓存队列的大小,当正在执行的线程数等于corePoolSize时,多余的元素缓存ArrayBlockingQueue队列中等待有空闲的线程时继续执行,当ArrayBlockingQueue已满时,加入ArrayBlockingQueue失败,会开启新的线程去执行,当线程数已经达到最大的maximumPoolSizes时,再有新的元素尝试加入ArrayBlockingQueue时会报错。

Tomcat的线程池

  • Tomcat启动时如果没有请求过来,那么线程数(都是指线程池的)为0;一旦有请求,Tomcat会初始化minSpareThreads设置的线程数;Tomcat的线程池的线程数跟你的瞬间并发有关系,比如maxThreads设置为1000,当瞬间并发达到1000那么Tomcat就会起1000个线程来处理,这时候跟你应用的快慢关系不大。注意:根据前面所说,只是并发那一瞬间Tomcat会起800个线程处理请求,但是稳定后,某一瞬间可能只有很少的线程处于RUNNABLE状态,大部分线程是TIMED_WAITING,如果你的应用处理时间够快的话。所以真正决定Tomcat最大可能达到的线程数是maxConnections这个参数和并发数,当并发数超过这个参数则请求会排队,这时响应的快慢就看你的程序性能了。
  • 比较容易弄混的是maxThreads和maxConnections这两个参数:比如maxThreads=1000,maxConnections=800,假设某一瞬间的并发时1000,那么最终Tomcat的线程数将会是800,即同时处理800个请求,剩余200进入队列“排队”,如果acceptCount=100,那么有100个请求会被拒掉。
  • Tomcat会停止长时间闲置的线程,这个时间就是maxIdleTime,当然这个Thread Count不会小于minSpareThreads。
    https://blog.csdn.net/xiaoxudong666/article/details/79688941

计算机网络

TCP如何保证长连接?提示说:websocket和http设置有一个共同的参数

  • TCP长连接保持:KeepAlive。TCP协议的实现里有一个KeepAlive机制,自动检测能否和对方连通并保持连接。
  • TCP识别不同的请求:每个连接建立时,都会保存一个唯一的套接字,有了这个套接字,你就知道对方的IP地址、端口号等信息。这样,通过这个套接字,就可以向指定方发送信息了。

网络编程方面,socket建立通信的步骤有什么

  • 客户端:new Socket()->Connect()->Send()->Recv()->close()
  • 服务端:new Socket()->Bind()->Listen()->Accept()->Recv()->Send()->close()

操作系统

select, poll和epoll

  • select: 将fd从用户态拷贝到核心态,全部轮询,文件描述符只有1024
  • poll: 本质和select没有区别,将用户传入数组拷贝到内核态,没有最大连接限制,基于链表存储
  • epoll: 将就绪的设备放入就绪链表,“醒着时”只需判断链表,而不用全部轮询;没有上限,select和poll每次都把fd从用户态拷贝到核心态,而epoll只拷贝一次

操作系统做了哪些事情,让内存的申请和释放更高效

用缓存来提高数据的访问速度,把主存块装入缓存后,每次访问CPU缓存时,如何把主存的物理地址(Physical address)或虚拟地址(Virtual address)变换成CPU缓存的地址。缓存的置换策略有最久未使用算法(LRU)、先进先出算法(FIFO)、最近最少使用算法(LFU)。

进程切换的开销为什么比线程大

因为进程切换时,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置。而线程切换只须保存和设置少量寄存器的内容,并不涉及存储器管理方面的操作。

进程通信的方式和对应的场景

  • 共享内存: 多个进程共享一块内存,是专门用来解决不同进程之间的通信问题的,由于是直接对内存进行数据传输操作
  • 管道: 亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系,fork出来一个子进程
  • 消息队列:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识
  • 套接字:socket连接

Spring

数据库

B+树插入删除的时候需要调整树高,有些维护成本

数据库死锁

  • 数据库死锁的现象:程序在执行的过程中,点击确定或保存按钮,程序没有响应,也没有出现报错。
  • 死锁的原理:当对于数据库某个表的某一列做更新或删除等操作,执行完毕后该条语句不提交,另一条对于这一列数据做更新操作的语句在执行的时候就会处于等待状态,此时的现象是这条语句一直在执行,但一直没有执行成功,也没有报错。

模糊查询一定会导致索引失效吗?会的

数据库的mvcc知道吗?

  • Mysql的大多数事务型存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,他们一般都同时实现了MVCC.实现了非阻塞的读操作,写操作也只锁定必要的行。MVCC的实现,是通过保存数据在某个时间点的快照来实现的。即为:不管需要执行多长时间,每个事务看到的数据都是一致的。
  • innodb的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个是行的创建时间,一个保存行的过期时间。存储的是系统版本号,不是真实的时间。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。
  • SELECT:innodb会根据以下两个条件检查每行记录:
    • a.innodb只查找版本号早于当前事务版本的数据行,<=当前事务版本号,这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的
    • b.行的删除版本要么未定义,要么大于当前的事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除。
  • INSERT:INNODB为新插入的每一行保存当前系统版本号作为行版本号
  • DELETE:innodb为删除的每一行保存当前系统版本号作为行删除标识
  • UPDATE:innodb为插入一行新纪录,保存当前系统版本号为行版本号,同时保存当前系统版本号到原来的行作为行删除标识
  • MVCC只在repeatable read和read committed两个隔离级别下工作。其他两个隔离级别和MVCC不兼容。因为READ UNCOMMITTED 总是读取最新的数据行,而不是符合当前事务版本的数据行。而SERIALIZABLE 则会对所有读取的行都加锁。
    https://www.cnblogs.com/maggie94/p/6746549.html

看从数据库的状态

show slave status,这个指令包含两个状态

  • slave_io_running: 将bin-log抄写到relay-log上,有没有问题。如果是No,一般是网络连接不上,权限不够等环境问题
  • slave_sql_running: 负责把中继日志的语句在从库上执行一遍

如何看主从是否完全同步

数据库读写分离的3种方式

  • 业务层面spring aop:根据方法名,动态选择到数据源。优点:代码改动少,适合拓展;缺点:需要对方法名进行人工管理。
  • MyBatis配置文件创建读写分离两个DataSource,将所有读的操作配置在读文件中,所有写的操作配置在写文件中。优点:容易实现;缺点:需要修改xml
  • Mybatis的Plugin在业务层实现数据库读写分离,在MyBatis创建Statement对象前通过拦截器选择真正的数据源,在拦截器中根据方法名称不同(select、update、insert、delete)选择数据源。优点:不需要更改代码
    https://blog.csdn.net/belvine/article/details/86219907

relay-log中数据格式

relay-log和bin-log都是二进制形式,对它们格式化之后就是一些SQL语句

Redis

redis中快照的实现方式

  • Redis使用fork函数复制一份当前进程(父进程)的副本(子进程);
  • 父进程继续接收并处理客户端发来的命令,而子进程开始将内存中的数据写入硬盘中的临时文件;
  • 当子进程写入完所有数据后会用该临时文件替换旧的 RDB 文件,至此一次快照操作完成。

redis的主从模式,哨兵模式,集群模式具体是什么样的

https://zhuanlan.zhihu.com/p/62936527

  • 主从模式:让一个服务器去复制另一个服务器的数据。被复制的服务器称为:Master主服务;对主服务器进行复制的服务器称为:Slave从服务器。主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的,并接受主数据库同步过来的数据
  • 哨兵模式:为了解决Redis的主从复制的不支持高可用性能,Redis实现了Sentinel哨兵机制解决方案。由一个或多个Sentinel去监听任意多个主服务以及主服务器下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线的主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已经下线的从服务器,并且Sentinel可以互相监视。
  • 集群模式:集群是Redis提供的分布式数据库方案,集群通过分片来进行数据共享,并提供复制和故障转移功能。一个Redis集群通常由多个节点组成;最初,每个节点都是独立的,需要将独立的节点连接起来才能形成可工作的集群。Redis中的集群分为主节点和从节点。其中主节点用于处理槽;而从节点用于复制某个主节点,并在被复制的主节点下线时,代替下线的主节点继续处理命令请求。

redis中用setnx设置分布式锁,如果这个节点挂了怎么办?

分布式锁回答要点:

  • 获取锁的时候,用 setnx 加锁,key为被锁的数据的唯一标识,value为当前服务器ip和任务标识的拼接,在释放锁的时候进行判断。并用expire命令为锁添加过期时间,超过该时间则自动释放锁。
  • 获取锁的时候调用 setnx,如果返回0,则该锁正在被别人使用,返回1则成功获取锁,还设置一个获取的超时时间。
  • 释放锁的时候,判断是不是该锁,若是,则执行 delete 进行释放。
    分布式锁中常见的问题
  • 问题1:A服务加锁,key: job value: 127.0.0.1, 执行时间超过锁的时间。而这个时候服务B加锁,key: job value: 127.0.0.2。过了一段时间A服务执行完了,A解锁,但是这个锁是B加的。
  • 回答1:解锁的时候加入判断,看看解锁和加锁是不是同一服务。
  • 问题2:可能遇到服务器崩溃的问题,A服务加锁,调用setnx,而这个时候服务器崩溃了,没有设置锁的超时时间。这就会导致其他的服务不能加锁。
  • 回答2:使用Lua脚本,把两个命令合起来使用。因为设置锁是setnx,设置超时时间是expire,这两个不是原子的,所以要用脚本;或者用set指令,可以同时实现两个指令的功能

其他框架

项目问题

为什么要限制跨域请求?

做一个假网站,里面用iframe嵌套一个银行网站 http://mybank.com。把iframe宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。这时如果用户输入账号密码,我们的主网站可以跨域访问到http://mybank.com的dom节点,就可以拿到用户的输入了,那么就完成了一次攻击。

介绍研究生的研究方向

当时讲的比较乱,下回介绍要分层次,先从大的方面介绍,比如系统由A和B组成,他们各自的作用,然后再分别展开A和B。

posted @ 2020-08-25 08:39  冰糖ryj  阅读(89)  评论(0编辑  收藏  举报