面经–陌陌一面

20次阅读
没有评论

陌陌一面:

面试官很和蔼,有些不会的会给你提醒,然后会跟你说清楚,面试体验很不错

1、自我介绍

2、叫我介绍我弄的项目,我随便介绍了其中一个

3、说一些SpringMVC的一个执行过程

SpringMVC执行流程:
面经--陌陌一面
1、用户请求发送到前端控制器DispatcherServlet

2、DispatcherServlet收到请求去调用处理器映射器RequestMapping

3、处理器映射器去寻找具体的处理器(可以根据xml文件或者注解查找),生成处理器以及处理器拦截器。并返回给DispatcherServlet.

4、DispatcherServlet调用处理器适配器HandlerAdapter

5、处理器适配器调用Controller层业务代码

6、Controller层获取到结果返回ModelAndview

7、HandlerAdapter将结果返回给DispatcherServlet

8、DispatcherServlet调用视图解析器,将结果传给视图解析器

9、视图解析器解析结果后返回具体的view

10、DispatcherServlet根据View进行渲染视图

11、DispatcherServlet响应客户

5、扯了一些项目其他的,应该是我MVC流程那块没有说清楚,所以想结合项目一起问问,比如前端的登录怎么找到我controller层的那个方法

主要是通过视图解析器来实现,视图解析器将String类型的视图名和Locale解析为view类型的视图。View是用来渲染界面的。也就是将程序返回的参数回填到模板。
如果视图解析器不能通过ViewName来获取Name,那么只能够通过Request来获取

注解RequestMapping

@RequestMapping

RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

怎么处理数据的乱码?
1、如果是前端导致出现乱码,可以更改前端编码设置

2、如果是后端出现乱码,可以选择在web.xml文件中设置编码类型获取自己编写过滤器

3、如果是数据库出现乱码,更改数据库配置文件

springmvc九大组件
面经--陌陌一面

6、ArrayList和LinkedList的区别:

ArrayList:

  • 基于动态数组,连续内存存储,适合下标访问(随机访问)

  • 扩容机制:由于数组长度固定,超出存取长度时创建新的数组,然后将老数组的数据拷贝到新数组中

  • 如果不是尾插数据会涉及元素的移动(往后复制一份,然后插入新数据),但是如果采用尾插法并指定初始容量会极大提升性能,甚至超过LinkedList,因为LinkedList插入的元素都要封装成node对象,性能消耗较大

LinkeList:

  • 基于双向链表,可以存储在分散的内存中

  • 数据插入删除操作效率高,查询效率低

  • 遍历LinkedList必须用iiterator不能用for循环,因为每次for循环都是通过get(i)来获取元素就需要对list进行重新的遍历,性能消耗极大

  • 另外不要试图使用indexOf等方法返回索引,因为其对list进行了遍历,如果结果为空就白白遍历了整个链表,效率很低

7、HashMap的底层实现原理

JDK1.8之前
JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列
HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n – 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的
长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。
所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。

JDK1.8之后
相比于之前的版本, JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转
化为红黑树,以减少搜索时间。

HashMap 和 Hashtable 的区别

  1. 线程是否安全:
    HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过 synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);
  2. 效率:
    因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在 代码中使用它;
  3. 对Null key 和Null value的支持:
    HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键 所对应的值为 null。。但是在 HashTable 中 put 进的键值只要有一个 null,直接抛出 NullPointerException。
  4. 初始容量大小和每次扩充容量大小的不同 :
    ①创建时如果不指定容量初始值,Hashtable默认的初始大小为 11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。
    ②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小(HashMap 中tableSizeFor() 方法保证,下面给出了源代码)。也就是说 HashMap 总是使用2的幂作为哈希表的大小,后面会介绍到为什么是2的幂次方。
  5. 底层数据结构:
    JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。

8、synchronized关键字相关的

  1. synchronized底层实现原理

synchronized有两种形式上锁:
同步方法、同步代码块。它们底层实现其实都一样,在进入同步代码之前先获取锁,获取到锁之后锁的计数器+1,同步代码执行完锁的计数器-1,如果获取失败就阻塞式等待锁的释放。只是他们在同步块识别方式上有所不一样,从class字节码文件可以表现出来,一个是通过方法flags标志,一个是monitorenter和monitorexit指令操作。

面经--陌陌一面
可以看出在执行同步代码块之前之后都有一个monitor字样,其中前面的是monitorenter,后面的是离开monitorexit,不难想象一个线程也执行同步代码块,首先要获取锁,而获取锁的过程就是monitorenter ,在执行完代码块之后,要释放锁,释放锁就是执行monitorexit指令。

为什么会有两个monitorexit呢?

这个主要是防止在同步代码块中线程因异常退出,而锁没有得到释放,这必然会造成死锁(等待的线程永远获取不到锁)。因此最后一个monitorexit是保证在异常情况下,锁也可以得到释放,避免死锁。

9、synchronized和Lock的区别

面经--陌陌一面

10、场景题:如果首页上有十个线程想要获得结果,怎么实现(还要设计超时)

使用JUC辅助类:

1、CountDownLatch

允许一个或多个线程等待直到其他线程中执行的一组操作完成的同步辅助。

是一个减法计数器,初始化时的参数作为总数,使用countDown方法使其数量减一。

使用await方法等待计数器归零,再往下继续执行。

例子,假如教室中有6个人,代表6个线程,如果不使用await方法,那么可能教师里的人还没出去完,门就被关上了,但是使用了await方法,只有6个人出去,才会执行关门操作

11、怎么创建一个线程:
4种方式:
1、继承Thread类

(1)创建一个类继承Thread类,重写run()方法,将所要完成的任务代码写进run()方法中;

(2)创建Thread类的子类的对象;

(3)调用该对象的start()方法,该start()方法表示先开启线程,然后调用run()方法;

2、实现Runnable接口

(1)创建一个类并实现Runnable接口

(2)重写run()方法,将所要完成的任务代码写进run()方法中

(3)创建实现Runnable接口的类的对象,将该对象当做Thread类的构造方法中的参数传进去

(4)使用Thread类的构造方法创建一个对象,并调用start()方法即可运行该线程

3、实现Callable接口

(1)创建一个类并实现Callable接口

(2)重写call()方法,将所要完成的任务的代码写进call()方法中,需要注意的是call()方法有返回值,并且可以抛出异常

(3)创建Callable实现类的实例,再创建Future接口的实现类FutureTask类的对象,使用FutureTask类包装Callanle对象

(4)使用Thread类的有参构造器创建对象,将FutureTask类的对象当做参数传进去,然后调用start()方法开启并运行该线程。

(5)调用FutureTask对象的get()方法可获取call()方法的返回值

4、使用线程池

(1)使用Executors类中的newFixedThreadPool(int num)方法创建一个线程数量为num的线程池

(2)调用线程池中的execute()方法执行由实现Runnable接口创建的线程;调用submit()方法执行由实现Callable接口创建的线程

(3)调用线程池中的shutdown()方法关闭线程池

12、线程池的七大参数

int maximumPoolSize, //核心线程池大小

int maximumPoolSize, //最大核心线程池大小

long keepAliveTime, //超时了, 没有人调用就会释放

TimeUnit unit, //超时时间单位

BlockingQueue workQueue, //阻塞队列

ThreadFactory threadFactory, //线程工厂,创建线程用,一般不用动

RejectedExecutionHandler handler //拒绝策略

13、拒绝策略:

AbortPolicy() //默认的拒绝策略,如果线程数量超过线程池最大数量加上阻塞队列大小,不处理多余的线程,抛出异常

CallerRunsPolicy() //哪儿来的回哪儿去

DiscardPolicy() //队列满了,丢掉任务,不会跑出异常

DiscardOldestPolicy() //队列满了,尝试和最早的线程竞争,也不会跑出异常

14、Stream并行流了解吗

Stream 是JAVA8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL执行数据查询一样。也可使用StreamAPI做并行操作,总之,StreamAPI提供了一种高效且易于使用的处理数据的方式。

并行流就是把一个内容分成多个数据块,并用不同的线程分成多个数据块,并用不同的线程分别处理每个数据块的流。
JAVA8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过parallel() 与sequential() 在并行流与顺序流之间进行切换。其实JAVA8底层是使用JAVA7新加入的Fork/Join框架:

15、怎么在Linux系统下查找Top5的异常

1.得到错误日志或者异常日志的行号

cat -n test.log |grep “error”

cat -n test.log |grep “exception”

  1. 通过位置往前往后查看日志详细

//339563 can not close IO 查询语句在 339500 -339600行中 ||100代表在339500
往后看100行

cat -n test.log |tail -n +339500 |head -n 100

16、项目中解决问题的思路

17、自己实现split函数。给定一个字符串,把单词分割出来

18、了解LocalThread吗

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

由于每一条线程都含有线程私有的ThreadLocalMap容器,这些容器间相互独立不影响,因此不会存在线程安全的问题,从而无需使用同步机制来保证多条线程访问容器的互斥性

19、ThreadLocal应用场景

  • 避免参数的显示传递:(比如线程中处理一个非常复杂的业务,有很多方法,使用 ThreadLocal可以代替一些参数的显式传递,直接从当前线程中存取)
  • 线程间数据隔离 进行事务操作时存储线程事务信息,因为事务和线程绑定在一起(Spring在事务开始时会给当前线程绑定一个Jdbc Connection对象,放在ThreadLocal中存储,这样在整个事务执行过程中都是使用该线程绑定的connection来执行数据库操作,实现了事务的隔离性)
  • 数据库连接(经典的使用场景是为每个线程分配一个JDBC Connection连接对象,这样可以保证每个线程的都在各自的Connection上进行数据库的操作,不会出现A线程关了B线程正在使用的Connection)
  • session会话等线程级别的操作(Session 的特性很适合 ThreadLocal ,因为 Session之前当前会话周期内有效,会话结束便销毁)
czmtmvmw
版权声明:本站原创文章,由 czmtmvmw 2022-07-19发表,共计5471字。
转载说明:除特殊说明外本站文章皆由乐买号发布,转载请注明出处。
评论(没有评论)