《互联网大厂Java求职者面试:核心知识大考验》

63 阅读9分钟

面试官:请简要介绍一下Java中的多线程,包括如何创建线程和线程之间的同步机制。

王铁牛:创建线程可以通过继承Thread类或者实现Runnable接口。线程同步可以用synchronized关键字,还有Lock接口。

面试官:不错,回答得很简洁明了。那再问你,JUC包中的CountDownLatch和CyclicBarrier有什么区别?

王铁牛:嗯……CountDownLatch是倒计数,等计数为0时释放所有等待的线程;CyclicBarrier是让一组线程互相等待,等都到达某个点后再一起执行。

面试官:很好,理解得很到位。接下来问你关于JVM的问题,简述一下Java内存模型。

王铁牛:Java内存模型包括堆、栈、方法区等,堆里存对象,栈里存局部变量,方法区存类信息。

第一轮结束。

面试官:说说线程池的工作原理以及如何合理配置线程池参数。

王铁牛:线程池有核心线程数、最大线程数等参数,工作原理是先让核心线程干活,满了就放队列里,队列满了再看是否超过最大线程数。

面试官:那HashMap的底层数据结构是什么,它是如何解决哈希冲突的?

王铁牛:底层是数组加链表,哈希冲突时就把新节点加到链表后面。

面试官:ArrayList的扩容机制是怎样的?

王铁牛:好像是每次扩容1.5倍。

第二轮结束。

面试官:讲讲Spring框架中IoC和AOP的概念。

王铁牛:IoC是控制反转,把对象创建和依赖注入交给Spring容器;AOP是面向切面编程,能在不修改原有代码的基础上增强功能。

面试官:Spring Boot有哪些优点?

王铁牛:它很方便,能快速搭建项目,有自动配置。

面试官:MyBatis的动态SQL有哪些常见用法?

王铁牛:像if、foreach这些吧。

第三轮结束。

面试官:Dubbo的服务调用原理是什么?

王铁牛:这个……不太清楚。

面试官:RabbitMq的消息可靠性是如何保证的?

王铁牛:额……说不太明白。

面试官:xxl-job的执行流程是怎样的?

王铁牛:没怎么用过,不太清楚。

面试官:Redis的数据类型有哪些,以及它们的应用场景。

王铁牛:就知道有字符串,其他的不太清楚。

面试结束,整体来看,你对一些基础知识掌握得还不错,但对于一些复杂点的问题回答得不够清晰和深入。回去等通知吧。

答案:

  • 多线程
    • 创建线程:可以通过继承Thread类或者实现Runnable接口。继承Thread类时,重写run方法,然后创建该类实例调用start方法启动线程;实现Runnable接口,实现run方法,将其作为参数传入Thread类构造函数创建线程实例并调用start方法启动。
    • 线程同步:
      • synchronized关键字:可以修饰方法或代码块。修饰方法时,该方法在同一时刻只能被一个线程访问;修饰代码块时,同一时刻只有一个线程能进入该代码块。
      • Lock接口:提供了比synchronized更灵活的锁控制。例如ReentrantLock,可实现公平锁、可中断锁等功能。
  • JUC包中的CountDownLatch和CyclicBarrier区别
    • CountDownLatch:用于一个或多个线程等待其他线程完成操作。通过构造函数传入计数值,调用countDown方法递减计数,调用await方法等待计数为0。比如多个线程并发处理任务,最后需要等所有任务完成后再执行下一步操作,就可以用CountDownLatch。
    • CyclicBarrier:让一组线程互相等待,直到所有线程都到达某个点,然后一起继续执行。通过构造函数传入 parties(线程数量)和一个可选的 barrierAction(所有线程到达屏障点后执行的操作)。例如运动员参加接力比赛,所有运动员都准备好后一起出发。
  • Java内存模型
    • 堆:存放对象实例,是垃圾回收主要区域。
    • 栈:存储局部变量,每个方法被调用时会创建一个栈帧,栈帧中存放局部变量表、操作数栈等。
    • 方法区:存储类信息、常量、静态变量等。
  • 线程池工作原理及参数配置
    • 工作原理:线程池有核心线程数、最大线程数、队列等组件。提交任务时,先由核心线程执行,若核心线程数已满,则将任务放入队列,若队列也满了,且线程数未达到最大线程数,则创建新线程执行任务,若线程数达到最大线程数,任务可根据拒绝策略处理。
    • 参数配置:
      • corePoolSize:核心线程数,默认情况下核心线程会一直存活,即使空闲。
      • maximumPoolSize:最大线程数,当任务数超过核心线程数和队列容量之和时,会创建新线程直到达到最大线程数。
      • keepAliveTime:线程池线程数超过核心线程数时,多余线程在多长时间后会被销毁。
      • unit:keepAliveTime的时间单位。
      • workQueue:存放任务的队列,常见的有ArrayBlockingQueue、LinkedBlockingQueue等。
      • threadFactory:创建线程的工厂。
      • handler:任务拒绝策略,如AbortPolicy(直接抛出异常)、CallerRunsPolicy(调用者执行任务)等。
  • HashMap底层数据结构及哈希冲突解决
    • 底层数据结构:数组 + 链表(JDK8 后链表长度大于8且数组长度大于64时会转化为红黑树)。
    • 哈希冲突解决:通过计算key的哈希值,将其映射到数组的某个位置。如果该位置已有元素,则形成链表或红黑树结构,新元素添加到链表或红黑树末尾。
  • ArrayList扩容机制:当ArrayList容量不足时,会进行扩容。默认扩容为原来容量的1.5倍,新容量 = 原容量 + (原容量 >> 1)。扩容时会创建一个新的更大容量的数组,将原数组元素复制到新数组。
  • Spring框架中IoC和AOP
    • IoC(控制反转):将对象的创建和依赖注入交给Spring容器管理。传统方式对象之间依赖关系由代码主动创建,IoC中对象只声明依赖,由容器负责创建和注入,降低了对象间的耦合度。
    • AOP(面向切面编程):在不修改原有业务代码的基础上,通过动态代理等方式为业务方法添加额外功能,如日志记录、事务管理等。
  • Spring Boot优点
    • 快速搭建项目:提供了大量的自动配置,减少了繁琐的配置文件编写。
    • 内置Web容器:可以直接运行Web应用,无需外部再配置Web服务器。
    • 约定大于配置:遵循一定的约定,减少开发人员手动配置的工作量。
    • 集成众多框架:方便集成各种常用框架,如Spring Data、MyBatis等。
  • MyBatis动态SQL常见用法
    • if:根据条件判断动态拼接SQL语句。例如:
<select id="selectUser" parameterType="map" resultType="User">
    SELECT * FROM user
    WHERE 1 = 1
    <if test="username != null">
        AND username = #{username}
    </if>
    <if test="age != null">
        AND age = #{age}
    </if>
</select>
- foreach:用于循环遍历集合,常用于批量操作。例如:
<delete id="deleteUsers" parameterType="list">
    DELETE FROM user WHERE id IN
    <foreach collection="list" item="item" open="(" separator="," close=")">
        #{item}
    </foreach>
</delete>
  • Dubbo服务调用原理
    • 服务注册:服务提供者将自身服务接口及实现类等信息注册到注册中心。
    • 服务发现:服务消费者从注册中心获取服务提供者的地址等信息。
    • 远程调用:消费者通过代理对象调用远程服务,代理对象通过网络与提供者进行通信,将调用请求发送到提供者,提供者执行相应方法并返回结果给消费者。过程中涉及网络传输、序列化反序列化等操作。
  • RabbitMq消息可靠性保证
    • 生产者确认机制:生产者发送消息后,可通过事务机制或Confirm机制确保消息被正确发送到Broker。事务机制通过channel.txSelect开启事务,发送消息后通过channel.txCommit提交事务,若发送失败可回滚;Confirm机制通过channel.confirmSelect开启,消息发送后可通过addConfirmListener监听确认结果。
    • 消息持久化:在创建队列、交换机时设置durable为true,发送消息时设置MessageProperties.PERSISTENT_TEXT_PLAIN,确保消息和队列等在Broker重启后不丢失。
    • 消费者确认:消费者收到消息后,可通过手动确认方式,调用channel.basicAck方法确认已接收消息,防止消息丢失。若消费者在未确认时断开连接,RabbitMq会将消息重新发送给其他消费者。
  • xxl-job执行流程
    • 调度中心配置任务:定义任务的执行时间、执行器等信息。
    • 任务注册:执行器启动后向调度中心注册自身信息,包括可执行的任务列表等。
    • 调度触发:调度中心按照配置的时间触发任务调度,根据任务配置找到对应的执行器。
    • 任务执行:执行器接收到调度中心的任务请求,执行相应的业务逻辑。
    • 结果反馈:执行器执行完任务后,将执行结果反馈给调度中心。
  • Redis数据类型及应用场景
    • 字符串(String):最基本的数据类型,可以存储各种数据,如缓存、计数器、分布式锁等。例如缓存热点数据,实现分布式锁通过SETNX命令。
    • 哈希(Hash):适合存储对象,如存储用户信息。
Map<String, Object> user = new HashMap<>();
user.put("name", "张三");
user.put("age", 20);
redisTemplate.opsForHash().putAll("user:1", user);
- 列表(List):可以作为消息队列、任务队列等。如简单的消息队列实现,生产者通过rpush命令将消息添加到列表,消费者通过lpop命令获取消息。
- 集合(Set):用于去重、交集、并集等操作。比如统计网站的UV(独立访客),将用户ID存入Set,通过SCARD命令获取Set大小。
- 有序集合(Sorted Set):可用于排行榜等场景。例如存储用户积分,通过ZADD命令添加元素,ZREVRANGE命令按积分倒序获取排行榜。