面试官:请简要介绍一下Java中的多线程,以及如何创建一个线程。
王铁牛:多线程就是在一个程序中同时运行多个线程。创建线程可以通过继承Thread类或者实现Runnable接口。
面试官:不错,回答得很简洁明了。那再问你,线程池有什么作用?如何创建一个线程池?
王铁牛:线程池可以复用线程,提高性能。创建线程池可以使用ThreadPoolExecutor类。
面试官:很好。接下来问一些关于JVM的问题,JVM的内存模型分为哪几个部分?
王铁牛:JVM内存模型分为堆、栈、方法区、程序计数器等。
第一轮结束,王铁牛对简单问题回答得较为清晰,得到了面试官的夸赞。
面试官:那说说HashMap的底层实现原理。
王铁牛:嗯……它是基于数组和链表实现的,当链表长度超过一定阈值时会转换为红黑树。
面试官:那扩容机制是怎样的呢?
王铁牛:扩容就是数组大小翻倍,然后重新计算元素位置。
面试官:Spring框架中,依赖注入有几种方式?
王铁牛:有构造器注入、setter方法注入等。
第二轮结束,王铁牛回答得有好有坏。
面试官:Dubbo的集群容错有哪些模式?
王铁牛:这个……好像有几种,但是我不太记得了。
面试官:RabbitMq的消息确认机制了解吗?
王铁牛:不太清楚。
面试官:xxl-job的执行器有哪些类型?
王铁牛:呃……不太明白。
第三轮结束,王铁牛对于复杂问题回答得比较混乱。
面试结束,面试官表示会让王铁牛回家等通知。此次面试中,王铁牛在面对简单问题时能够清晰作答,展现出了一定的基础知识储备,但在面对复杂问题时,回答得不够准确和清晰,需要进一步加强对相关技术的深入理解和掌握。
答案:
- 多线程及创建线程:
- 多线程:多线程是指在一个程序中同时运行多个线程,每个线程执行不同的任务,从而提高程序的执行效率和响应速度。
- 创建线程的方式:
- 继承Thread类:定义一个类继承Thread类,重写run方法,然后创建该类的对象并调用start方法启动线程。例如:
class MyThread extends Thread { @Override public void run() { System.out.println("This is a thread"); } } MyThread thread = new MyThread(); thread.start(); - 实现Runnable接口:定义一个类实现Runnable接口,实现run方法,然后创建Thread类的对象并将实现Runnable接口的类的对象作为参数传递给Thread类的构造函数,最后调用start方法启动线程。例如:
class MyRunnable implements Runnable { @Override public void run() { System.out.println("This is a runnable thread"); } } MyRunnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start();
- 继承Thread类:定义一个类继承Thread类,重写run方法,然后创建该类的对象并调用start方法启动线程。例如:
- 线程池:
- 作用:线程池可以复用线程,避免频繁创建和销毁线程带来的性能开销。它可以控制线程的并发数量,提高系统的稳定性和响应速度。
- 创建线程池:使用ThreadPoolExecutor类来创建线程池。例如:
ThreadPoolExecutor executor = new ThreadPoolExecutor( corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.CallerRunsPolicy());- 参数解释:
- corePoolSize:线程池的核心线程数,当提交的任务数小于corePoolSize时,会创建新的线程来执行任务。
- maximumPoolSize:线程池的最大线程数,当提交的任务数大于corePoolSize且任务队列已满时,会创建新的线程来执行任务,直到线程数达到maximumPoolSize。
- keepAliveTime:线程池中的线程在空闲时的存活时间,当线程空闲时间超过keepAliveTime时,线程会被销毁。
- TimeUnit:keepAliveTime的时间单位。
- workQueue:任务队列,用于存储提交的任务,当线程池中的线程都在忙碌时,新提交的任务会被放入任务队列中。
- handler:线程池的拒绝策略,当线程池中的线程数达到maximumPoolSize且任务队列已满时,会调用handler来处理新提交的任务。
- 参数解释:
- JVM内存模型:
- 堆:是JVM中最大的一块内存区域,用于存储对象实例。它被划分为新生代、老年代和永久代(Java 8及以后为元空间)。新生代又分为Eden区和两个Survivor区,对象一般首先在Eden区创建,当Eden区满时,会触发Minor GC,将存活的对象移动到Survivor区,当Survivor区也满时,会将对象移动到老年代。老年代用于存储生命周期较长的对象。
- 栈:每个线程都有自己独立的栈,用于存储局部变量、方法调用等信息。栈内存是线程私有的,随着线程的创建而创建,随着线程的结束而销毁。
- 方法区:用于存储类的信息、常量、静态变量等。在Java 8及以后,方法区被元空间取代,元空间使用本地内存,而不是像方法区那样使用JVM的堆内存。
- 程序计数器:是一块较小的内存区域,它记录了当前线程正在执行的字节码指令的地址,每个线程都有自己独立的程序计数器。
- HashMap底层实现原理:
- HashMap是基于数组和链表(JDK 8及以后引入红黑树)实现的。
- 初始数组:HashMap内部维护一个Entry数组,初始容量为16(默认值)。
- 哈希值计算:通过key的hashCode方法计算出哈希值,然后通过扰动函数对哈希值进行进一步处理,使其分布更均匀。
- 存储位置计算:通过哈希值与数组长度进行按位与操作,得到元素在数组中的存储位置。例如:
index = hash & (table.length - 1)。 - 冲突处理:
- 链表:当两个元素的哈希值通过上述计算得到相同的存储位置时,会在该位置形成链表。新元素会添加到链表的头部。
- 红黑树:当链表长度超过一定阈值(默认8)时,链表会转换为红黑树,以提高查询效率。
- 扩容机制:
- 当HashMap中的元素个数超过负载因子(默认0.75)与数组长度的乘积时,会触发扩容。
- 扩容时,会创建一个新的更大的数组(大小翻倍),然后将原数组中的元素重新计算哈希值并放入新数组中。
- Spring框架依赖注入方式:
- 构造器注入:通过构造函数将依赖对象注入到目标对象中。例如:
public class UserService { private final UserDao userDao; public UserService(UserDao userDao) { this.userDao = userDao; } } - setter方法注入:通过setter方法将依赖对象注入到目标对象中。例如:
public class UserService { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } }
- 构造器注入:通过构造函数将依赖对象注入到目标对象中。例如:
- Dubbo集群容错模式:
- Failover Cluster:失败自动切换,当出现调用失败时,会自动重试其他服务器。
- Failfast Cluster:快速失败,当调用失败时,立即返回错误,不再重试。
- Failsafe Cluster:失败安全,当调用失败时,直接忽略,不抛出异常。
- Failback Cluster:失败自动恢复,当调用失败时,会记录失败请求,然后定时重试。
- Forking Cluster:并行调用多个服务器,只要有一个成功就返回。
- RabbitMq消息确认机制:
- 生产者确认机制:
- 事务机制:通过channel.txSelect开启事务,然后发送消息,最后通过channel.txCommit提交事务,如果发送过程中出现异常,可以通过channel.txRollback回滚事务。这种方式比较简单,但性能较低。
- Confirm机制:通过channel.confirmSelect开启确认模式,发送消息后,通过addConfirmListener监听消息的确认结果。Confirm机制有两种模式:
- 普通确认模式:消息发送后,服务器会异步返回确认结果。
- 批量确认模式:可以将多条消息一起发送,然后一次性获取这些消息的确认结果。
- 消费者确认机制:
- 自动确认:消费者接收到消息后,自动确认消息已被接收。通过
channel.basicConsume(queue, true, consumerTag, consumer)方法的第二个参数设置为true来开启自动确认。 - 手动确认:消费者接收到消息后,需要手动调用
channel.basicAck(deliveryTag, false)方法来确认消息已被接收。其中deliveryTag是消息的唯一标识,false表示不批量确认。如果消息处理失败,可以调用channel.basicNack(deliveryTag, false, true)方法拒绝消息,true表示重新入队。
- 自动确认:消费者接收到消息后,自动确认消息已被接收。通过
- 生产者确认机制:
- xxl - job执行器类型:
- BEAN模式:任务以Spring Bean的方式运行,适用于轻量级任务。
- GLUE模式:用户可以在线编写任务逻辑,支持多种语言,任务运行时会动态编译执行。
- CRON模式:按照指定的Cron表达式定时执行任务。
- HTTP模式:通过HTTP请求触发任务执行。
- SDK模式:适用于集成到其他应用中,通过调用SDK提供的接口触发任务。