面试官:第一轮提问开始。首先,简单说一下Java中的多线程有哪些实现方式?
王铁牛:嗯……有继承Thread类和实现Runnable接口这两种方式。
面试官:不错,回答正确。那再问一个,说说线程池的几个重要参数及其作用。
王铁牛:这个我知道,corePoolSize是核心线程数,当提交的任务数小于它时,会创建新线程执行任务;maximumPoolSize是最大线程数,当任务数超过核心线程数且队列满时,会创建新线程直到达到这个数;keepAliveTime是线程存活时间,当线程数超过核心线程数,空闲线程会在这个时间后销毁;unit是时间单位;workQueue是任务队列,用来存放提交的任务;handler是拒绝策略,当任务数超过最大线程数且队列满时,用来处理新提交的任务。
面试官:回答得很清晰,值得夸赞。下面进入第二轮提问。请讲讲HashMap的底层实现原理。
王铁牛:它是基于数组和链表实现的,当哈希冲突时会将新节点添加到链表末尾。后来在JDK1.8中引入了红黑树,当链表长度超过8时会转换为红黑树来提高查询效率。
面试官:嗯,了解。那Spring框架中,IoC和AOP的概念是什么?
王铁牛:IoC就是控制反转,把对象的创建和依赖关系管理交给Spring容器;AOP是面向切面编程,通过动态代理在不修改原有代码的基础上增强功能。
面试官:还算说得过去。最后一轮提问,Dubbo的集群容错策略有哪些?
王铁牛:这个……好像有什么失败重试、负载均衡啥的,具体的我也不太清楚了。
面试官:好了,面试就到这里,回去等通知吧。
答案:
- Java多线程实现方式:
- 继承Thread类:创建一个类继承Thread类,重写run方法,在run方法中编写线程执行的代码。然后通过创建该类的实例并调用start方法来启动线程。例如:
class MyThread extends Thread { @Override public void run() { System.out.println("线程执行的代码"); } } MyThread thread = new MyThread(); thread.start();- 实现Runnable接口:创建一个类实现Runnable接口,重写run方法。然后通过创建Thread类的实例并将实现Runnable接口的类的实例作为参数传递给Thread的构造函数,最后调用start方法启动线程。例如:
class MyRunnable implements Runnable { @Override public void run() { System.out.println("线程执行的代码"); } } MyRunnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start(); - 线程池参数及其作用:
- corePoolSize:核心线程数。当提交的任务数小于corePoolSize时,线程池会创建新线程来执行任务。这些线程会一直存在,除非设置了allowCoreThreadTimeOut为true。
- maximumPoolSize:最大线程数。当任务数超过corePoolSize且任务队列已满时,线程池会创建新线程直到线程数达到maximumPoolSize。如果超过这个数,任务会根据拒绝策略进行处理。
- keepAliveTime:线程存活时间。当线程数超过corePoolSize,空闲线程会在keepAliveTime指定的时间后销毁,前提是设置了allowCoreThreadTimeOut为true或者线程池采用了非核心线程池模式(默认)。
- unit:keepAliveTime的时间单位,例如TimeUnit.SECONDS表示秒。
- workQueue:任务队列,用来存放提交的任务。常用的有ArrayBlockingQueue(有界阻塞队列)、LinkedBlockingQueue(无界阻塞队列)等。
- handler:拒绝策略。当任务数超过maximumPoolSize且队列满时,用来处理新提交的任务。常见的拒绝策略有AbortPolicy(直接抛出异常)、CallerRunsPolicy(调用者运行任务)、DiscardPolicy(丢弃新提交的任务)、DiscardOldestPolicy(丢弃队列中最老的任务)。
- HashMap底层实现原理:
- 数组和链表:HashMap底层是一个数组,每个数组元素是一个链表节点(在JDK1.8之前)。当向HashMap中插入键值对时,首先通过哈希函数计算键的哈希值,然后根据哈希值找到对应的数组下标。如果该下标为空,则直接插入新节点;如果不为空,则遍历链表,找到相同键的节点则更新其值,否则在链表末尾添加新节点。
- 红黑树:在JDK1.8中,当链表长度超过8时,链表会转换为红黑树。这是因为链表在查找元素时时间复杂度为O(n),而红黑树的时间复杂度为O(logn),可以提高查询效率。当链表长度小于6时,红黑树又会转换回链表。
- Spring框架中IoC和AOP的概念:
- IoC(控制反转):传统的对象创建和依赖关系管理由程序员手动完成,而IoC把这些工作交给Spring容器。Spring容器通过配置文件或注解等方式创建对象,并管理对象之间的依赖关系。例如,一个类依赖另一个类,在IoC模式下,Spring容器会自动创建并注入依赖对象,而不是在类内部手动创建。
- AOP(面向切面编程):通过动态代理机制,在不修改原有业务代码的基础上,为业务方法添加额外的功能,如日志记录、事务管理等。这些额外的功能被称为切面。AOP将横切关注点(如日志、事务)与业务逻辑分离,提高了代码的可维护性和复用性。
- Dubbo的集群容错策略:
- 失败重试:当调用失败时,自动重新发起调用,直到成功或达到重试次数上限。可以通过配置retries参数来设置重试次数。
- 负载均衡:在多个服务提供者之间选择一个进行调用。常见的负载均衡策略有随机、轮询、加权随机、加权轮询等。可以通过配置loadbalance参数来选择负载均衡策略。
- 集群容错策略还有:
- failfast:快速失败,只发起一次调用,失败立即报错。
- failsafe:失败安全,出现异常时直接忽略,不抛出异常。
- forking:并行调用多个服务提供者,只要有一个成功就返回。可以通过配置forks参数来设置并行调用的服务提供者数量。
- broadcast:广播调用所有服务提供者,逐个调用,任意一个报错则报错。