文章目录
提问问题
- 笔试题:一趟扫描实现:删除链表的倒数第 n 个结点
说说Redis缓存和数据库的数据⼀致性
介绍一下Redis的Redis主从复制和持久化机制?
Redis是单线程还是多线程?为什么?
介绍类加载机制
介绍一下MySQL索引为什么用B+树?为什么不⽤B树?为什么不⽤⼆叉树?为什么不⽤hash?
介绍一下MySQL存储引擎有哪些?区别是什么?
SpringBoot和Spring Cloud区别?介绍一下Spring框架?
介绍MongoDB?
介绍一下RPC?
介绍一下NIO?
多线程的创建⽅式有哪些?
说⼀说线程池的七⼤参数
Java并发了解吗?JUC包了解哪些?
CountDownLatch和CyclicBarrier的区别?
线程和进程的区别
进程间的通信⽅式有哪些?
问题1
利用双指针,让快慢指针相差n个节点,这样当快指针遍历完链表,删除的节点就是慢指针指到的节点;更详细思路可以参考之前的文章删除链表倒数第n个节点
public ListNode removeNthFromEnd(ListNode head, int n){
ListNode dummyNode = new ListNode(0);
dummyNode.next = head;
ListNode fastIndex = dummyNode;
ListNode slowIndex = dummyNode;
// 只要快慢指针相差 n 个结点即可
for (int i = 0; i <= n ; i++){
fastIndex = fastIndex.next;
}
while (fastIndex != null){
fastIndex = fastIndex.next;
slowIndex = slowIndex.next;
}
//此时 slowIndex 的位置就是待删除元素的前一个位置。
slowIndex.next = slowIndex.next.next;
return dummyNode.next;
}
问题2
Redis缓存和数据库的数据一致性问题是分布式系统中常见的问题。在Redis作为缓存使用时,通常采用“写前缓存”(Write-Through Cache)或“写回缓存”(Write-Back Cache)的策略。写前缓存意味着每次写操作都会同时更新数据库和缓存;写回缓存则是先更新缓存,然后异步更新数据库。
为了保证一致性,可以采用以下策略:
- 使用缓存失效策略,例如,在更新数据库后使缓存失效,下次访问时直接从数据库加载。
- 使用延迟双删策略,即在更新数据库后先删除缓存,过一段时间后再次删除缓存,以防止因为删除操作的失败导致的数据不一致。
- 使用版本号或时间戳等机制,确保数据的一致性。
问题3
Redis的主从复制机制是指将一个Redis服务器(主服务器)的数据复制到一个或多个Redis服务器(从服务器)上。这样做可以实现读写分离,提高读操作的性能,并且可以用作数据备份和故障转移。
Redis的持久化机制包括RDB(Redis Database)快照和AOF(Append Only File)日志两种方式:
- RDB是周期性地将内存中的数据快照保存到磁盘上的二进制文件中。
- AOF记录了所有对Redis执行的写操作命令,并追加到文件末尾,可以配置同步频率。
问题4
Redis本质上是单线程的,它使用非阻塞I/O和多路复用技术来处理成千上万的客户端连接。单线程模型简化了编程模型,避免了多线程带来的复杂性,如竞态条件和死锁等问题,同时也减少了上下文切换带来的开销。
问题5
类加载机制是Java运行时环境的一部分,负责将编译后的字节码加载到内存中,并为其创建对应的Class对象。Java类加载器遵循双亲委派模型,由启动类加载器、扩展类加载器和系统类加载器组成。
问题6
MySQL索引通常使用B+树作为其数据结构,用于加快数据检索速度。常见的索引类型包括B+Tree、Hash、Full-Text等。
B+树是一种平衡树,它可以保证所有的叶子节点都在同一层上,这样可以使得范围查询变得非常高效。其次,B+树的叶子节点之间是通过指针链接起来的,这种设计使得可以很容易地进行顺序访问,即可以快速地进行范围查询和排序操作。最后B+树的分裂和合并操作比B树要简单,这对于数据库系统来说是非常重要的,因为数据库系统经常需要对数据进行插入和删除操作。
B树索引适用于范围查询,而Hash索引适用于等值查询。
不使用B树的原因可能是因为B树索引在插入、删除操作时需要频繁的平衡操作,而在某些情况下,这可能会导致性能下降。
不使用二叉树的原因是因为在多维度查询中,二叉树的效率不如B树高。
不使用Hash的原因是因为Hash索引不支持范围查询。
问题7
MySQL存储引擎包括InnoDB、MyISAM、Memory等。
InnoDB支持事务处理、行级锁定和外键约束,适合处理大量并发事务的场景。
MyISAM不支持事务,支持表级锁定,适合读取密集型的应用。
Memory存储引擎将数据存储在内存中,访问速度快,但数据在重启后丢失。
问题8
Spring Boot是一个开源的Java框架,用于创建微服务应用。它提供了快速开发的能力,通过自动配置简化了项目搭建和开发过程。而Spring Cloud是构建在Spring Boot之上的一系列框架,用于支持分布式系统开发,如服务发现、配置管理、负载均衡等。
Spring框架是一个轻量级的企业级应用开发框架,提供了全面的编程和配置模型,支持面向切面编程、依赖注入、声明式事务管理等多种先进的编程范式。
问题9
MongoDB是一个NoSQL数据库,采用文档存储形式,以BSON格式存储数据,支持丰富的查询语言和索引。它适合处理大量的半结构化数据,具有高可用性、水平扩展能力强等特点。
问题10
RPC(Remote Procedure Call)允许在网络上的不同计算机上远程调用程序或函数,就像调用本地程序一样简单。RPC抽象了网络传输细节,提供了一种透明的调用方式。
问题11
NIO(New Input/Output)是Java SE中的一套新的I/O API,提供了非阻塞的读写操作,允许单个线程处理多个网络连接,提高了I/O操作的效率。
问题12
多线程的创建方式包括继承Thread类和实现Runnable接口。此外,还有Callable接口和FutureTask类可以创建可返回结果的线程,以及使用ExecutorService框架提交任务。
问题13
线程池的七大参数包括核心线程数、最大线程数、存活时间、工作队列、拒绝策略、线程名前缀和线程工厂。这些参数共同决定了线程池的行为和性能。
问题14
Java并发是指在同一时间内由多个任务并行执行的能力。在单核处理器上,通过时间分片技术实现任务的并发执行,而在多核处理器上,可以真正实现任务的并行执行。
JUC(Java Util Concurrency)包提供了一系列工具类,如ConcurrentHashMap、CopyOnWriteArrayList、Semaphore、CountDownLatch、CyclicBarrier、Executors等,用于简化并发编程和提高并发性能。
问题15
CountDownLatch允许一个或多个线程等待一组在其他线程中执行的操作完成。它的核心是一个计数器,计数器的初始值为n,每个等待线程调用await()方法会使计数器减1,当计数器减到0时,等待的线程会被唤醒继续执行。
CyclicBarrier允许一组线程相互等待,直到达到某个公共屏障点,所有线程才能一起继续执行。
与CountDownLatch不同的是,CyclicBarrier可以重复使用。
问题16
关键区别:线程是进程中的执行单元,共享进程资源;进程是操作系统分配资源的独立单位,拥有自己的内存空间。
问题17
进程之间通信(IPC)的方式主要包括管道,命名管道,信号量,消息队列,共享内存,信号,套接字,远程过程调用,共享数据库,文件;详细介绍如下:
管道(Pipes):管道是最古老的一种IPC方式,它允许一个进程的输出直接成为另一个进程的输入。有名管道和无名管道是两种类型,前者有文件系统中的入口,后者则没有。
命名管道(Named Pipes,FIFO):类似于管道,但它有一个路径名与之关联,因此可以在不相关的进程间进行通信。
信号量(Semaphores):信号量主要用于进程间的同步,它可以用来控制多个进程对共享资源的访问。
消息队列(Message Queues):消息队列提供了一个消息的缓冲区,进程可以向队列中发送或接收消息。
共享内存(Shared Memory):共享内存是最快的IPC方式,因为它允许进程直接读写相同的物理内存区域。
信号(Signals):信号是一种比较简单的进程间通信机制,用于通知接收进程某个事件已经发生。
套接字(Sockets):套接字提供了一种在网络上的进程间通信方式,不仅限于同一台机器上的进程。
远程过程调用(Remote Procedure Call, RPC):RPC允许一个进程像调用本地函数一样调用另一台机器上的进程。
共享数据库:进程可以通过共享数据库进行间接通信,每个进程向数据库写入数据,然后其他进程从中读取数据。
文件:进程可以通过写入和读取文件来交换数据,尽管这种方式效率较低,但它简单易用,适用于少量数据的交换。
写在最后
PS:以上是网络上收集的一些常见的问题以及自己对答案搜索整理;一次整理基本上就是面试一次的题量,适合对自己的知识的查缺补漏
面试一般根据岗位要求或者简历上写的来进行扩展提问,也有些是直接问公司常用到的相关方面的技术问题,无论怎么准备都祝大家能拿到心怡的offer!