一线互联网常见的 14 个 Java 面试题,你颤抖了吗程序员

跳槽不算频繁,但参加过很多面试(电话面试、face to face 面试),面过大 / 小公司、互联网 / 传统软件公司,面糊过(眼高手低,缺少实战经验,挂掉),也面过人,所幸未因失败而气馁,在此过程当中不断查缺补漏,养成了踏实、追本溯源、持续改进的习惯,特此将本身经历过、构思过的一些面试题记录下来,若是答案有问题,欢迎拍砖讨论,但愿能对找工做或者感兴趣的同窗有所帮助,陆续整理中。java

1. synchronized 和 reentrantlock 异同

相同点web

  • 都实现了多线程同步和内存可见性语义
  • 都是可重入锁

不一样点面试

  • 实现机制不一样 synchronized 经过 java 对象头锁标记和 Monitor 对象实现 reentrantlock 经过
    CAS、ASQ(AbstractQueuedSynchronizer)和 locksupport(用于阻塞和解除阻塞)实现
    synchronized 依赖 jvm 内存模型保证包含共享变量的多线程内存可见性 reentrantlock 经过 ASQ 的
    volatile state 保证包含共享变量的多线程内存可见性
  • 使用方式不一样 synchronized 能够修饰实例方法(锁住实例对象)、静态方法(锁住类对象)、代码块(显示指定锁对象)reentrantlock 显示调用 trylock()/lock() 方法,须要在 finally 块中释放锁
  • 功能丰富程度不一样 reentrantlock
    提供有限时间等候锁(设置过时时间)、可中断锁(lockInterruptibly)、condition(提供 await、signal
    等方法)等丰富语义 reentrantlock 提供公平锁和非公平锁实现 synchronized
    不可设置等待时间、不可被中断(interrupted)

2. concurrenthashmap 为什么读不用加锁

jdk1.7数据库

  • 1)HashEntry 中的 key、hash、next 均为 final 型,只能表头插入、删除结点
  • 2)HashEntry 类的 value 域被声明为 volatile 型
  • 3)不容许用 null 做为键和值,当读线程读到某个 HashEntry 的 value 域的值为 null
    时,便知道产生了冲突——发生了重排序现象(put 设置新 value 对象的字节码指令重排序),须要加锁后从新读入这个 value 值
  • 4)volatile 变量 count 协调读写线程之间的内存可见性,写操做后修改 count,读操做先读 count,根据
    happen-before 传递性原则写操做的修改读操做可以看到

jdk1.8编程

  • 1)Node 的 val 和 next 均为 volatile 型
  • 2)tabAt 和 casTabAt 对应的 unsafe 操做实现了 volatile 语义

3. ContextClassLoader(线程上下文类加载器)的做用

  • 越过类加载器的双亲委派机制去加载类,如 serviceloader 实现
  • 使用线程上下文类加载器加载类,要注意保证多个须要通讯的线程间的类加载器应该是同一个,防止由于不一样的类加载器致使类型转换异常(ClassCastException)

4. tomcat 类加载机制

clipboard.png

  • 不一样应用使用不一样的 webapp 类加载器,实现应用隔离的效果,webapp 类加载器下面是 jsp 类加载器
  • 不一样应用共享的 jar 包能够放到 Shared 类加载器 /shared 目录下

5. osgi 类加载机制

clipboard.png

  • osgi 类加载模型是网状的,能够在模块(Bundle)间互相委托
  • osgi 实现模块化热部署的关键是自定义类加载器机制的实现,每一个 Bundle 都有一个本身的类加载器,当须要更换一个 Bundle时,就把 Bundle 连同类加载器一块儿换掉以实现代码的热替换

当收到类加载请求时,osgi 将按照下面的顺序进行类搜索:数组

  • 1)将以 java.* 开头的类委派给父类加载器加载
  • 2)不然,将委派列表名单(配置文件 org.osgi.framework.bootdelegation 中定义)内的类委派给父类加载器加载
  • 3)不然,检查是否在 Import-Package 中声明,若是是,则委派给 Export 这个类的 Bundle 的类加载器加载
  • 4)不然,检查是否在 Require-Bundle 中声明,若是是,则将类加载请求委托给 required bundle 的类加载器
  • 5)不然,查找当前 Bundle 的 ClassPath,使用本身的类加载器加载
  • 6)不然,查找类是否在本身的 Fragment Bundle 中,若是在,则委派给 Fragment Bundle 的类加载器加载
  • 7)不然,查找 Dynamic Import-Package(Dynamic Import 只有在真正用到此 Package
    的时候才进行加载)的 Bundle,委派给对应 Bundle 的类加载器加载
  • 8)不然,类查找失败

6. 如何结束一个一直运行的线程

  • 使用退出标志,这个 flag 变量要多线程可见
  • 使用 interrupt,结合 isInterrupted() 使用

7. threadlocal 使用场景及问题

  • threadlocal 并不能解决多线程共享变量的问题,同一个 threadlocal 所包含的对象,在不一样的 thread
    中有不一样的副本,互不干扰
  • 用于存放线程上下文变量,方便同一线程对变量的先后屡次读取,如事务、数据库 connection 链接,在 web 编程中使用的更多
  • 问题: 注意线程池场景使用 threadlocal,由于实际变量值存放在了 thread 的 threadlocalmap
    类型变量中,若是该值没有 remove,也没有先 set 的话,可能会获得之前的旧值
  • 问题: 注意线程池场景下的内存泄露,虽然 threadlocal 的 get/set 会清除 key(key 为 threadlocal的弱引用,value 是强引用,致使 value 不释放)为 null 的 entry,可是最好 remove

8. 线程池从启动到工做的流程

刚建立时,里面没有线程
调用 execute() 添加任务时:tomcat

  • 1)若是正在运行的线程数量小于核心参数 corePoolSize,继续建立线程运行这个任务
  • 2)不然,若是正在运行的线程数量大于或等于 corePoolSize,将任务加入到阻塞队列中
  • 3)不然,若是队列已满,同时正在运行的线程数量小于核心参数 maximumPoolSize,继续建立线程运行这个任务
  • 4)不然,若是队列已满,同时正在运行的线程数量大于或等于 maximumPoolSize,根据设置的拒绝策略处理
  • 5)完成一个任务,继续取下一个任务处理
  • 6)没有任务继续处理,线程被中断或者线程池被关闭时,线程退出执行,若是线程池被关闭,线程结束
  • 7)不然,判断线程池正在运行的线程数量是否大于核心线程数,若是是,线程结束,不然线程阻塞。所以线程池任务所有执行完成后,继续留存的线程池大小为corePoolSize
  • 8)本文所列出的 14 个 Java面试题只是我所遭遇的面试中的一部分,其余的面试题我也会陆续整理出来,说到这里另外顺便给你们推荐一个架构交流学习群:650385180,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty 源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,相信对于已经工做和遇到技术瓶颈的码友,在这个群里会有你须要的内容。

9. 阻塞队列 BlockingQueue take 和 poll 区别

  • poll(time):取走 BlockingQueue 里排在首位的对象, 若不能当即取出,则能够等 time参数规定的时间,取不到时返回 null
  • take():取走 BlockingQueue 里排在首位的对象,若 BlockingQueue 为空,阻塞直到BlockingQueue 有新的对象被加入

10. 如何从 FutureTask 不阻塞获取结果

  • get(long timeout,TimeUnit unit),超时则返回
  • 轮询,先经过 isDone()判断是否结束,而后调用 get()

11. blockingqueue 若是存放了比较关键的数据,系统宕机该如何处理

  • 开放性问题,欢迎讨论
  • 将队列持久化,比较麻烦,须要将生产数据持久化到磁盘,持久化成功才返回,消费者线程从磁盘加载数据到内存阻塞队列中,维护消费offset,启动时,根据消费 offset 从磁盘加载数据
  • 加入消息队列,保证消息不丢失,生成序列号,消费幂等,根据消费进程决定系统重启后的生产状态

12. NIO 与传统 I/O 的区别

  • 节约线程,NIO 由原来的每一个线程都须要阻塞读写变成了由单线程(即 Selector)负责处理多个 channel
    注册(register)的兴趣事件(SelectionKey)集合(底层借助操做系统提供的 epoll()),netty bossgroup 处理 accept 链接(没看明白为何 bossgroup 设置多个 thread的必要性),workergroup 处理具体业务流程和数据读写
  • NIO 提供非阻塞操做
  • 传统 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据,NIO 提供 bytebuffer,分为堆内和堆外缓冲区,读写时均先放到该缓冲区中,而后由内核经过 channel传输到对端,堆外缓冲区不走内核,提高了性能

13. list 中存放可重复字符串,如何删除某个字符串

  • 调用 iterator 相关方法删除
  • 倒删,防止正序删除致使的数组重排,index 跳过数组元素问题

14. 有哪些 GC ROOTS(跟平常开发比较相关的是和此相关的内存泄露)

  • 全部 Java 线程当前活跃的栈帧里指向 GC 堆里的对象的引用,所以用不到的对象及时置 null,提高内存回收效率
  • 静态变量引用的对象,所以减小静态变量特别是静态集合变量的大小,集合存放的对象覆写 euqls()和 hashcode(),防止持续增加
  • 本地方法 JNI 引用的对象
  • 方法区中的常量引用的对象,所以减小在长字符串上调用 String.intern()
  • classloader 加载的 class 对象,所以自定义 classloader 无效时及时置 null而且注意类加载器加载对象之间的隔离
  • jvm 里的一些静态数据结构里指向 GC 堆里的对象的引用
相关文章
相关标签/搜索