java程序员的金三银四求职宝典(二)

程序员的金三银四求职宝典

随着春天的脚步渐近,对于许多程序员来说,一年中最繁忙、最重要的面试季节也随之而来。金三银四,即三月和四月,被广大程序员视为求职的黄金时期。在这两个月里,各大公司纷纷开放招聘,求职者们则通过一轮又一轮的面试,力争心仪的职位。而如何在这关键的时期脱颖而出,成为每个求职者关注的焦点。在金三银四的关键时期如何准备,快来看看吧

面试题解析

接着上一篇面试题解析(java程序员的金三银四求职宝典),咱们继续……

21、使用submit和execute向线程池提交任务的区别?

答:这两种提交方式都是JDK提供的。

  1. 来源不同:execute是顶级接口executor中定义的,submit(好几种重载形式)是executorService接口中定义的;
  2. 接收参数不同:execute方法只能接收Runnable接口任务,而submit可以接收Runnable和Callable的任务;
  3. 返回值不同:execute返回void,submit返回Future。
  4. 异常:execute执行任务时,如果遇到任务会抛出异常,subumit不会直接抛出异常,只有在使用Future的get方法获取返回值时,才抛出异常。

22、进程和线程

答:进程有自己独立的内存空间。进程时应用程序,,一个进程可以有多个线程。

进程是操作系统级别的,线程是CPU级别的。

线程的生命周期:

  1. 新建状态;
  2. 可运行状态(分为就绪和运行);
  3. 阻塞状态;
  4. 等待状态;----wait
  5. 超时等待状态;---sleep
  6. 死亡状态。

23、死锁产生的原因?怎么避免?

  1. 互斥:一个资源每次只能被一个进程使用;
  2. 请求和保持:一个进程因请求资源而阻塞时,不释放获得资源;
  3. 不剥夺:进程已获得的资源,在未使用之前,不能强行剥夺;
  4. 循环等待:进程之间循环等待资源

避免死锁那就打破产生死锁的原因:

  1. 加锁保证互斥;
  2. 一次性申请所有资源;
  3. 按序申请资源;
  4. 占有部分资源时,线程进一步申请资源,如果申请不到那就释放占有的资源。

24、Synchronized的底层原理?

答:简单来说,底层就是内部有一个计数器,当计数器为0的时候表示可以成功获取到锁,获取到后将计数器设置为1;当线程执行完后,在将计数器设置为0.

25、Volatile和synchronized的区别?

答:

  1. Volatile只能作用在变量上,synchronized可以作用在类、方法、代码块、变量上;
  2. Volatile只能保证可见性,synchronized可以保证可见性和原子性;
  3. Volatile禁用指令重排,synchronized不会;
  4. Volatile不会造成阻塞,synchronized会。

26、synchronized和lock的区别?

答:

1、synchronized作用在类、方法、代码块、变量上,lock作用在方法中;

2、synchronized不知道是否将锁释放,lock是手动释放锁的,所以知道;lock一般使用ReentrantLock类做为锁。

27、Threadlocal与synchronized的区别?

答:

  1. synchronized用于线程间的数据共享,Threadlocal则用于线程间的数据隔离;
  2. synchronized是利用锁的机制,是变量或者代码块在某一时刻只能被一个线程访问。而Threadlocal为每一个线程都提供一个变量副本。
  3. 上述两个都是解决多线程并发访问的问题。

28、Threadlocal内存泄露?

答:每个线程都有一个ThreadLocalMap,Map中的元素key为ThreadLocal,值对应线程的变量副本。

垃圾回收时会自动回收key,而value的回收取决与thread对象的生命周期。一般都是使用线程池进行线程复用操作的,这就导致线程对象的生命周期比较长,这样便会一直存在一条强引用链的关系。随着任务的执行,value可能就会越来越多且无法释放,最终导致内存泄露

29、内存泄露和内存溢出的区别?

答:内存溢出就是在申请内存空间时,内存空间不够。内存泄露是指程序申请内存后,无法释放已申请的内存,一次内存泄漏的危害可以忽略,,但是内存泄露堆积后果很严重。内存泄露最终会导致内存溢出。

30、红黑树是怎么调整平衡的?

答:

  1. 左旋;
  2. 右旋;
  3. 改变颜色。

31、什么是回表查询?如何避免回表查询?

答:回表查询的本质:是普通索引找不到我们要的完整信息,迫不得已要执行回表操作。也就是多次B+树的查询操作,会导致效率低。

怎么解决?就是可以建立联合索引。

32、使用了范围查找还能走索引吗?

答:如果严格意义上来说,使用了范围查找是不会走索引的。但是像in这种范围查询本质上是等值查询,它也会走索引的。

33、Redis分布式锁?Redis持久化方式?

答:先理解一下,为啥会问redis的持久化方式呢?因为redis的特点就是单线程也快,归结于redis是一个内存数据库。但是内存数据库一般有个缺点就是,内存相对于磁盘存储来说,存储空间太小了,空间很容易就不足了。因此,会有持久化操作方式。redis持久化就是将数据写在磁盘,可以有效地避免因进程退出造成的数据丢失问题,当下次重启时利用之前持久化的文件即可实现数据恢复。

Redis的持久化方式:1、RDB;2、AOF。

1、RDB(Redis DataBase):把当前进程数据生成快照保存到硬盘的过程。所谓内存快照就是将内存中的数据在某一时刻的状态记录下来。类似于拍照,把一瞬间的美记录下来。

那么RDB是给哪些数据做快照呢?

RDB是将所有数据都记录到磁盘中,但是,RDB文件越大,往磁盘上写数据的时间开销就越大。

RDB文件的生成是否会阻塞主线程?

Redis提供两个手动命令来生成RDB文件,save和bgsave。

Save:会导致主线程阻塞,线上环境不建议使用。在主线程中使用的

Bgsave:创建一个子线程,专门用于写入RDB文件,避免了主线程阻塞。Redis默认选择bgsave作为RDB文件的生成方式。

2、AOF:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到回复数据目的。AOF的主要作用是解决了数据持久化的实时性,目前是redis持久化的主流方式。AOF默认不开启,默认RDB

分布式锁

首先要达到分布式锁,必须要求redis有互斥(就是一个客户端加了锁,另一个客户端就加不了锁)的能力。我们可以使用SETNX命令,这个命令表示SET if Not Exists,即如果key不存在,才会设置它的值,否则什么也不做。

两个客户端进程执行这个命令,可以达到互斥,实现分布式锁。

客户端1:申请加锁,加锁成功;

客户端2:申请加锁,因为它后达到,加锁失败。

此时,加锁成功的客户端,就可以操作共享资源。操作完成后,需要及时释放锁。直接执行DEL命令删除key即可。

但是,这种方式存在很大的问题!!!当客户端1拿到锁后,如果发生如下情况,就会造成死锁

  1. 进程处理业务逻辑异常,没有及时释放锁
  2. 进程挂了,没有机会释放锁

这时,客户端1就会一直占用锁,其他客户端就永远拿不到锁。怎么解决这个问题?

  1. 将key设置有效时间;

上述操作还会有问题,因为redis释放锁是一个无脑操作,万一释放了别人的锁呢万一执行业务时间较长,设置锁的有效期到了怎么办

解决方案:客户端加锁时,设置一个只有客户端自己知道的唯一标识进去。

解决方案:分布式锁加看门狗。加锁时,先设置一个过期时间,然后客户端开启一个守护线程,定时去检测这个锁的失效时间,如果锁快要过期了,操作共享资源还未完成,那就自动对锁进行续费,重新设置有效时间。

34、Redis的主从复制过程?

答:主从复制是为了达成高可用

  1. 为了避免单点redis服务器故障,准备多台服务器,互相连通。将数据复制多个副本保存再不同的服务器上,连接再一起,并保证数据是同步的。
  2. 即使有一台服务器宕机,其他服务器依然可以继续提供服务。

主服务器:master(数据提供方)

从服务器:slave(数据接收方)

需要解决:数据同步问题;核心工作:master的数据复制都slave中。

主从复制:数据的复制只能是单向的,只能从主节点到从节点。

主从复制工作流程:

  1. 建立连接阶段(连接)--总体是建立了socket连接

1.1设置master的地址和端口,保存master信息;

1.2建立socket连接

1.3发送ping命令(定时器任务)

1.4身份验证

1.5发送slave端口信息

  1. 数据同步阶段---slave初次连接master后,复制master的所有数据到slave中,slave数据库状态更新为master当前数据库状态

2.1请求同步数据;

2.2创建RDB同步数据;

2.3恢复RDB同步数据;

2.4请求部分同步数据;

2.5回复部分同步数据;

  1. 命令传播阶段---当master数据库状态修改后,导致主从服务器数据库状态不一致,此时需要让主从数据同步到一致的状态,同步的动作成为命令传播。

35、联合索引失效了怎么办?

答:

联合索引失效场景:

  1. 条件中不使用联合索引的第一个字段;
  2. 条件中使用了范围查询--联合索引只能对多个字段进行等值查询进行优化,对范围查询的优化效果有限;
  3. 条件中使用了函数或者表达式--无法使用索引优化;
  4. 联合索引的字段顺序不合理--如果联合索引的字段顺序与查询语句中的条件顺寻不一致,那么联合索引将失效。

避免来联合索引失效方法:

  1. 查询优化条件:避免范围查询、函数和表达式,尽量将联合索引第一个字段包含再查询条件中;
  2. 重新设计索引;
  3. 拆分联合索引。

36、当运行一个项目时cpu的load过高,该怎么去解决呢?

答:

  1. 从编程语言层面上,full gc(垃圾回收机制)次数的增大或者死循环都可能造成cpu load增高;
  2. 寻找最占cpu的进程--ps ux;top -c;
  3. 寻找最耗cpu的线程;

37、Hashmap面试题?

答:

hashmap是一个线程不安全的map集合。

怎么实现线程安全的hashmap集合?

  1. 使用ConcurrentHashMap集合;
  2. 使用Collections类提供的synchronizedMap方法包装HashMap,将HashMap对象包装成一个线程安全的map对象。
  3. 使用ReentrantReadWriteLock类实现:使用ReentrantReadWriteLock类来控制HashMap中的读写操作,这样就能够在多个线程同时读取HashMap的情况下,避免出现竞争条件。而写操作则需要独占锁,保证只有一个线程执行写操作,防止数据冲突。
  4. 使用sychronized关键字实现:这样可以控制对hashmap的并发访问,保证同一时刻只有一个线程再访问hashmap。

Hashmap的底层是:数组+链表/红黑树;

当链表元素超过8时,自动装换成红黑树存储,重新创建一个hashmap复制。当红黑树元素为6时,自动转换成链表存储。Hashmap默认数组大小为16,扩容因子为0.75.当数组元素超过数组大小的0.75倍时,就扩容。Hashmap扩容后的长度直接变成之前的2倍。

红黑树:

  1. 只有黑或红色节点;
  2. 一个路径上的节点不允许有两个连续的红色节点;
  3. 根节点是黑色节点;
  4. 叶子节点都是黑色;
  5. 红色节点的父亲都是黑色节点。

为啥用红黑树不用链表?

就是时间复杂度的问题

Put方法最大能放多少对象?--就是数组的容量

如果内存足够大,能够一直扩容下去吗?理论上是可以的。

如果一直扩容,对象多了后,会影响读取吗?扩容过程本身不会影响读取操作,因为hashmap在进行扩容时,会使用一个新的更大的数组,并在后台将元素逐个重新计算哈希码、确定新的存储位置,并复制到新的数组中。在这个过程中,原来的数组仍然可以被读取,而新的数组是在后台构建的。

38、java内存分区?类的加载过程?

答:

  1. 堆区--又称GC堆;

所有线程所共享的一块内存区域,在虚拟机启动时创建。唯一的目的就是存放实例对象(new的对象还有数组)。也是java垃圾收集器管理的主要区域

  1. 栈区;

是线程私有的区域,它的生命周期和线程相同。每个方法在执行的时候都会同时创建一个栈用于存储局部变量表、操作栈、动态链接、方法返回地址等信息。每一个方法从被调用到执行完成的过程,就对应这一个栈在虚拟机中从入栈到出栈的过程。

  1. 方法区--又称Non-Heap;

与堆一样是所有线程共享的内存区域用于存储被虚拟机加载的类信息、常量、静态变量即使编译器编译后的代码等数据。

相对而言,垃圾收集器很少在这个区域回收垃圾。但是不代表不回收。这个区域的内存回收目标主要针对常量池的回收和类型的卸载

  1. 程序计数器;

程序计数器可以看作是当前线程所执行的字节码的行号指示器。在虚拟机中,字节码解释器工作时就是通过这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理和线程恢复

  1. 本地方法栈。

和栈的作用很像,区别是本地方法栈则为虚拟机提供使用到的Native方法服务

类的加载过程:加载--->验证--->准备--->解析--->初始化--->使用--->卸载

  1. 加载:获取二进制流转化为.class文件;
  2. 验证:确保被加载的类的正确性;.class文件头;
  3. 准备:为类的静态变量分配内存,并将其初始化默认值
  4. 解析:把类中的符号引用转换为直接引用(符号引用就是来描述所引用的目标,JVM并不知道所引用的类的地址,所以解析阶段就是把符号引用转换为直接引用的地址);
  5. 初始化:

5.1类什么时候初始化:new、调用类的静态方法、反射等;

5.2类的初始化顺序:

  1. 假如这个类存在直接父类,并且这个类没有被初始化(类只能被初始化一次),那就直接初始化直接父类;
  2. 加入类中存在初始化语句(static),那就依次执行

39、get请求和post请求有什么区别

答:

  1. get请求一般是去获取数据,post一般是去提交数据
  2. get请求的参数会放在url路径中,安全性较差,请求数据长度有限制,post请求的数据长度没有限制,请求数据放在body中。
  3. 通常get请求产生一个TCP数据包,post请求会产生两个TCP数据包。

40、介绍悲观锁和乐观锁一级使用场景?

答:

乐观锁:就是线程在操作数据时,不会去加锁,认为没有人修改数据;

悲观锁:比较悲观,认为线程在操作数据时,会去修改数据,所以会加锁。

乐观锁和悲观锁没有优劣之分,只有合适的场景而已。

乐观锁适合读多写少,并发冲突小的场景。

悲观锁适合读少写多,并发冲突较多的场景。

接一个题外话,最近想学习以下高并发高可用的项目,请问有哪些项目推荐的呀?大家可以打在评论区讨论以下,谢谢 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值