听说你在找最新的面试题?来吧全拿走(不定期更新)

1.排序算法有什么

冒泡排序,选择排序,插入排序,希尔排序,快速排序,归并排序,堆排序

2.线程池的参数以及创建线程的方法

  • 2.1为什么要使用线程池

    利用线程池把资源池化,使得线程资源能服用,可以避免频繁地创建和销毁
  • 2.2线程池的参数

corePoolSize 核心池(核心线程数)的大小

线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。

maximumPoolSize 线程池最大线程数 

一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后面会介绍)中,如果工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。

keepAliveTime 空闲线程存活时间

一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定

unit 空闲线程存活时间单位

keepAliveTime的计量单位

workQueue  工作队列     

新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务

threadFactory 线程工厂

创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等

  handler 拒绝策略

当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的

  • 2.3 线程池的创建方式

 newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

public static ExecutorService newCachedThreadPool() {
      return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                              60L, TimeUnit.SECONDS,
                              new SynchronousQueue<Runnable>());}
newFixedThreadPool

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

public static ExecutorService newFixedThreadPool(int nThreads) {
   return new ThreadPoolExecutor(nThreads, nThreads,
                              0L, TimeUnit.MILLISECONDS,
                              new LinkedBlockingQueue<Runnable>());}
newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行。

 public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
      new DelayedWorkQueue());}
newSingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
    (new ThreadPoolExecutor(1, 1,
                            0L, TimeUnit.MILLISECONDS,
                            new LinkedBlockingQueue<Runnable>()));}

阿里巴巴开发手册上有条限制

     线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样去处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险
     
     使用executors返回的线程池对象弊端如下:
     
     newFixedThreadPool和newSingleThreadExecutor
     允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求导致oom
     
     newCachedThreadPool 
     允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,导致oom
     
     看来,最终都有可能导致OOM,而 OOM 会导致所有请求都无法处理,这是致命问题。所以强烈建议使用有界队列,并设置最大线程数。
     
     所以要使用ThreadPoolExecutor去创建线程池

3.线程池内一个线程是怎么被创建的

具体执行逻辑主要在 ThreadPoolExecutor 的 execute(XQ特)() 方法
1.首先判断线程对象是否为空,false的话判断当前线程池的数量是否大于核心线程数,如果不大于则新建一个线程并将当前任务直接赋予该线程执行调用addWorker方法创建线程创建成功直接返回;
2.判断当前线程池是否处于运行态且任务入队列是否成功,若是则进行双重检查,确保池中仍有运行的线程数
3.若第二步入队列失败,线程池尝试扩容至最大线程数,若失败则拒绝该任务
     
当workerThread等于0时:提交任务,创建线程

当workerThread小于corePoolSize时:提交任务,创建线程,不管其他工作线程是不是闲置的。
当workerThread大于等于corePoolSize 且 workerThread小于maxinumPoolsize时:将任务添加到队列中,当队列满了后,创建线程。当一个线程完成任务时,它会从队列中取下一个任务来执行。
当workerThread等于maxinumPoolsize时:提交任务,既不能加入队列,也不能创建新的线程,将RejectedExecutionHandler的rejectedExecution方法执行拒绝策略

ThreadPoolExecutor常见方法:
execute()
submit()
shutdown()
shutdownNow()

4.Mysql相关的问题

  • 4.1 mysql的存储引擎

MySQL中的数据用各种不同的技术存储在文件(或者内存)中。每一种技术都使用不同的存储机制、索引技巧、锁定水平并且最终提供广泛的不同的功能和能力。通过选择不同的技术,能够获得额外的速度或者功能,从而改善应用的整体功能。 这些不同的技术以及配套的相关功能在MySQL中被称作存储引擎(也称作表类型)。

mysql版本5.6.24

– 查看MySQL版本
select version();

– 查看版本支持的存储引擎
show engines;

比较常问的两种

MyISAM: 拥有较高的插入,查询速度,但不支持事务
InnoDB :5.5.8版本后Mysql的默认数据库引擎,支持ACID事务,支持行级锁定

  • 4.2InnoDB引擎

    InnoDB 是一个事务安全的存储引擎,它具备提交、回滚以及崩溃恢复的功能以保护用户数据。InnoDB 的行级别锁定保证数据一致性提升了它的多用户并发数以及性能。InnoDB 将用户数据存储在聚集索引中以减少基于主键的普通查询所带来的 I/O 开销。为了保证数据的完整性,InnoDB 还支持外键约束。默认使用B+TREE数据结构存储索引。
特点:
  1. 支持事务,支持4个事务隔离(ACID)级别,
  2. 行级锁定(更新时锁定当前行)
  3. 读写阻塞与事务隔离级别相关
  4. 既能缓存索引又能缓存数据
  5. 支持外键
  6. InnoDB更消耗资源,读取速度没有MyISAM快
  7. 在InnoDB中存在着缓冲管理,通过缓冲池,将索引和数据全部缓存起来,加快查询的速度;
  8. 对于InnoDB类型的表,其数据的物理组织形式是聚簇表。所有的数据按照主键来组织。数据和索引放在一块,都位于B+数的叶子节点上;

InnoDB引擎调优:

  1. 主键尽可能小,否则会给Secondary index带来负担
  2. 避免全表扫描,这会造成锁表
  3. 尽可能缓存所有的索引和数据,减少IO操作
  4. 避免主键更新,这会造成大量的数据移动

事务(ACID)的特性

  1. A 事务的原子性(Atomicity):指一个事务要么全部执行,要么不执行.也就是说一个事务不可能只执行了一半就停止了.比如你从取款机取钱,这个事务可以分成两个步骤:1划卡,2出钱.不可能划了卡,而钱却没出来.这两步必须同时完成.要么就不完成.
  2. C 事务的一致性(Consistency):指事务的运行并不改变数据库中数据的一致性.例如,完整性约束了a+b=10,一个事务改变了a,那么b也应该随之改变.
  3. I 隔离线性(Isolation):事务的独立性也有称作隔离性,是指两个以上的事务不会出现交错执行的状态.因为这样可能会导致数据不一致.
  4. D 持久性(Durability):事务的持久性是指事务执行成功以后,该事务所对数据库所作的更改便是持久的保存在数据库之中,不会无缘无故的回滚.

事物的隔离级别

READ UNCOMMITTED (未提交读)

在READ UNCOMMITTED级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读( Dirty Read )

  READ COMMITTED(提交读)

READ COMMITTED 满足前面提到的隔离性的简单定义:一个事务开始时,只能“看见”已经提交的事务所做的修改。
  换句话说,
  一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。

这个级别有时候也叫做不可重复读(nonrepeatable read) ,因为两次执行同样的查询,可能会得到不一样的结果。

REPEATABLE READ(可重复读)

REPEATABLE READ 解决了脏读的问题。该级别保证了在同一个事务中多次读取同样记录的结果是一致的。
  但是理论上,可重复读隔离级别还是无法解决另外一个幻读 ( Phantom Read )的问题。
  所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,
  当之前的事务再次读取该范围的记录时,会产生幻行( Phantom Row )。
  InnoDB 和 XtraDB 存储引攀通过多版本并发控制( Mvcc , Multiversion Concurrency Control )解决了幻读的问题。
  本章稍后会做进一步的讨论。
  可重复读是 MySQL 的默认事务隔离级别。

SERIALIZABLE (可串行化)

SERIALIZABLE 是最高的隔离级别。它通过强制事务串行执行,避免了前面说的幻读的问题。
  简单来说, SERIALIZABLE 会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。
  实际应用中也很少用到这个隔离级别.只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。

4.3InnoDB数据存储结构

MySQL的InnoDB数据存储结构可以划分为逻辑存储结构和物理存储结构。

数据库磁盘读取与系统磁盘读取
  • 系统从磁盘中读取数据到内存时是以磁盘块(block)为基本单位,位于同一个磁盘块中的数据会被一次性读取出来。

  • InnoDB存储引擎中有页(Page)的概念,页是数据库管理磁盘的最小单位,InnoDB存储引擎中默认每个页的大小为16kb,每次读取磁盘时都将页载入内存中。

  • 系统一个磁盘块的大小空间往往没有16kb这么大,因此InnoDB每次io操作时都会将若干个地址连续的磁盘块的数据读入内存,从而实现整页读入内存。

定位一条表记录的过程

select * from user where id = 29
这里id是主键,我们通过这棵B+树来查找,首先找到根页,你怎么知道user表的根页在哪呢?

其实每张表的根页位置在表空间文件中是固定的。系统经过解析sql语句,首先从找到user表的跟页面(一个表通常需要多个页面组成,跟页面就是起始页),层级遍历非叶子节点页(索引)读取到key值为29的指针(遍历非叶子节点的过程随着节点的遍历会将一个或多个页加载到内存),最后到指针指向的叶子节点所在的页中,然后遍历找出该条记录。

如果使用了二级索引则先读取二级索引page遍历这个二级索引,找到装有主键信息叶子节点page页,遍历找到该主键。然后再根据主键索引寻找到该条记录

二叉搜索树
  • 所有非叶子结点至多拥有两个儿子(Left和Right);
  • 所有结点存储一个关键字;
  • 非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树;
B-Tree(B树)
  • 关键字集合分布在整颗树中;
  • 任何一个关键字出现且只出现在一个结点中;
  • 搜索有可能在非叶子结点结束;
  • 其搜索性能等价于在关键字全集内做一次二分查找;
  • 自动层次控制;

B树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点;重复,直到所对应的儿子指针为空,或已经是叶子结点;

B+ Tree
  • 所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;
  • 不可能在非叶子结点命中;
  • 非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;
  • 每一个叶子节点都包含指向下一个叶子节点的指针,从而方便叶子节点的范围遍历。
  • 更适合文件索引系统;

B+树的搜索与B-树也基本相同,区别是B+树只有达到叶子结点才命中(B-树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;

为什么B+ 树比B 树更适合作为索引?
  • B+ 树的磁盘读写代价更低
  • B+ 树的数据都集中在叶子节点,分支节点 只负责指针(索引);B 树的分支节点既有指针也有数据 。这将导致B+ 树的层高会小于B 树的层高,也就是说B+ 树平均的Io次数会小于B 树。
  • B+ 树的查询效率更加稳定
  • B+ 树的数据都存放在叶子节点,故任何关键字的查找必须走一条从根节点到叶子节点的路径。所有关键字的查询路径相同,每个数据查询效率相当。
  • B+树更便于遍历
  • 由于B+树的数据都存储在叶子结点中,分支结点均为索引,遍历只需要扫描一遍叶子节点即可;B树因为其分支结点同样存储着数据,要找到具体的数据,需要进行一次中序遍历按序来搜索。
  • B+树更擅长范围查询
  • B+树叶子节点存放数据,数据是按顺序放置的双向链表。B树范围查询只能中序遍历。

5.threadLocal是什么,使用的时候需要注意什么

ThreadLocal 是线程本地存储,在每个线程中都创建了一个 ThreadLocalMap 对象,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。

JDK 的实现中 Thread 持有 ThreadLocalMap,而且 ThreadLocalMap 里对 ThreadLocal 的引用还是弱引用(WeakReference),所以只要 Thread 对象可以被回收,那么 ThreadLocalMap 就能被回收。JDK 的这种实现方案复杂但更安全。

 在线程池中使用 ThreadLocal 为什么可能导致内存泄露呢?

在线程池中线程的存活时间太长,往往都是和程序同生共死的,这样 Thread 持有的 ThreadLocalMap 一直都不会被回收,再加上 ThreadLocalMap 中的 Entry 对 ThreadLocal 是弱引用(WeakReference),所以只要 ThreadLocal 结束了自己的生命周期是可以被回收掉的。
Entry 中的 Value 是被 Entry 强引用的,即便 value 的生命周期结束了,value 也是无法被回收的,导致内存泄露。

5.软引用、弱引用、虚引用-他们的特点及应用场景

强引用

强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfM moryError错误,使程序异常终止,也不会靠随意回收具有强引用 对象来解决内存不足的问题。

软引用

软引用是用来描述一些还有用但并非必须的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。

弱引用

弱引用也是用来描述非必须对象的,他的强度比软引用更弱一些,被弱引用关联的对象,在垃圾回收时,如果这个对象只被弱引用关联(没有任何强引用关联他),那么这个对象就会被回收。

6.spring boot

6.1Spring Boot Starter 的工作原理是什么

  1. Spring Boot 在启动时会去依赖的 Starter 包中寻找 resources/META-INF/spring.factories 文件,然后根据文件中配置的 Jar 包去扫描项目所依赖的 Jar 包。
  2. 根据 spring.factories 配置加载 AutoConfigure 类
  3. 根据 @Conditional 注解的条件,进行自动配置并将 Bean 注入 Spring Context

总结一下,其实就是 Spring Boot 在启动的时候,按照约定去读取 Spring Boot Starter 的配置信息,再根据配置信息对资源进行初始化,并注入到 Spring 容器中。这样 Spring Boot 启动完毕后,就已经准备好了一切资源,使用过程中直接注入对应 Bean 资源即可。

6.2 Spring Boot 的自动配置是如何实现的?

Spring Boot 项目的启动注解是:@SpringBootApplication,其实它就是由下面三个注解组成的:

@Configuration

@ComponentScan

@EnableAutoConfiguration

其中 @EnableAutoConfiguration 是实现自动配置的入口,该注解又通过 @Import 注解导入了AutoConfigurationImportSelector,在该类中加载 META-INF/spring.factories 的配置信息。然后筛选出以 EnableAutoConfiguration 为 key 的数据,加载到 IOC 容器中,实现自动配置功能!

7.RPC实现原理及运行流程

7.1什么是RPC框架?

RPC,全称为Remote Procedure Call,即远程过程调用,是一种计算机通信协议。
比如现在有两台机器:A机器和B机器,并且分别部署了应用A和应用B。假设此时位于A机器上的A应用想要调用位于B机器上的B应用提供的函数或是方法,由于A应用和B应用不在一个内存空间里面,所以不能直接调用,此时就需要通过网络来表达调用的方式和传输调用的数据。也即所谓的远程调用。

8.线上服务器内存溢出或者cpu过高怎么处理

定位问题

top命令查看最耗CPU的进程(进程:17038;CPU持续飙到595%+)
查看该进程中最耗CPU的线程(发现有一些线程占用CPU较高)

17038为进程号,键入P(大写P),该进程中的线程按照CPU从高到底排序
top -Hp 17038

将线程号转为16进制,同时查看这些线程当前正在干什么(在此以17045线程为例)
将线程号转为16进制;

其中17045为线程号
printf ‘%x\n’ 17045
17038为进程号,0x4295为最耗CPU线程的十六进制
jstack 17038 | grep ‘0x4295’ -C10 --color

用Jmap命令查看当前堆的使用情况(发现老年代现在已占用99.8%+)
其中17038为进程号
jmap -heap 17038

查看gc频率的命令(其中O代表老年代占用率,FGC是FullGC次数,FGCT是fullGC时间;可以看出在频繁FullGC但是老年代有资源一直释放不掉)
其中17038为进程号,5000是指每5秒(5000毫秒)输出一次
jstat -gcutil 17038 5000

通过分析出问题时线上日志发现内存溢出;至此定位到问题根源是内存溢出导致(有未释放资源堆积,导致老年代被占满,然后频繁的FullGC但是资源一直释放不了)
grep -m 10 ‘OutOfMemoryError’ *.log

生成dump文件命令

其中fileName是导出后dump名称,pid为进程号
jmap -dump:format=b,file=fileName.dump pid

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值