快手一面-面经

news/2025/1/8 10:11:24/

1. RPC和Http的区别?

RPC(Remote Procedure Call,远程过程调用)和 HTTP(HyperText Transfer Protocol,超文本传输协议)是两种不同的通信机制,它们有不同的用途、工作原理和应用场景。以下是它们之间的主要区别:

1.1 目的和用途

RPC:RPC是一种协议,允许程序在不同的计算机之间调用远程系统上的函数或方法。RPC的主要目的是让远程调用的过程像本地调用一样简单,从而使分布式系统中的各个组件能够互相通信。它可以支持不同的传输协议(如HTTP、TCP、WebSocket等),并且常用于微服务架构中。
HTTP:HTTP是一种应用层协议,最初用于浏览器与Web服务器之间的通信。它是Web应用程序的基础,主要用于传输文本、图像、视频等数据内容。HTTP本身不涉及函数调用和远程过程,它主要用于请求和响应模式的数据传输。

1.2 工作原理

RPC:RPC让客户端调用远程计算机上的服务时,就像调用本地函数一样。客户端发送请求,服务器执行相应的操作,然后返回结果。RPC通常包括一个封装过程,即将函数参数进行序列化,然后通过网络发送给服务器,再由服务器反序列化并执行。常见的RPC框架有gRPC、Apache Thrift等。
HTTP:HTTP是基于请求-响应模型的协议。客户端发送HTTP请求(如GET、POST等),服务器处理请求并返回HTTP响应。HTTP通常使用JSON或XML作为数据格式进行数据交换。

1.3 协议的底层依赖

RPC:RPC可以在多种底层协议上运行,如HTTP、TCP、Unix套接字等,甚至可以跨语言传输。RPC框架通过封装和序列化函数调用的参数、结果及其类型信息来支持跨平台、跨语言调用。
HTTP:HTTP通常基于TCP/IP协议栈,但它本身不定义如何进行函数调用。它只是定义了如何传输数据,并主要用于Web服务的请求和响应。

1.4 数据交换格式

RPC:RPC框架通常采用高效的数据格式来传输数据,常见的格式有Protobuf(用于gRPC)和Thrift(用于Apache Thrift)。这些格式通常比传统的JSON和XML更高效,尤其是在需要高性能、低延迟的场景下。
HTTP:HTTP协议的数据通常是文本格式,最常见的格式是JSON或XML。JSON格式简洁易读,在Web开发中广泛使用。

1.5 易用性和开发复杂度

RPC:RPC提供了类似函数调用的接口,因此开发者不需要关心底层通信的细节。开发者只需要定义服务的接口,框架会自动处理序列化、网络通信等操作。不同的RPC框架(如gRPC、Thrift等)对开发者的友好度、功能支持等有所不同。
HTTP:HTTP通常更简单,尤其是在Web开发中,很多语言和框架提供了对HTTP协议的直接支持。它更适用于简单的请求-响应场景,但当涉及到复杂的远程过程调用时,可能需要额外的协议(如REST、GraphQL等)或工具来补充功能。

1.6 性能

RPC:通常,RPC比HTTP具有更好的性能,尤其是在调用的参数和结果需要高效传输时。RPC使用的二进制协议(如Protobuf)通常比文本协议(如JSON)更高效,占用更少的带宽和处理时间。
HTTP:HTTP本身传输的通常是文本格式(如JSON或XML),这意味着它的数据传输效率较低,尤其是在高频调用的场景下可能会成为性能瓶颈。

1.7 支持的场景

RPC:RPC通常用于服务间的高效、低延迟通信,特别是在微服务架构、分布式系统和高性能计算中。由于其高效的调用方式,RPC适合用于需要频繁和高效地调用远程服务的场景。
HTTP:HTTP通常用于Web应用程序、RESTful API、客户端和服务器之间的交互。HTTP是一种成熟的、广泛支持的协议,适用于跨平台、跨设备的应用场景,尤其在Web和移动应用中非常常见。

1.8 扩展性和兼容性

RPC:RPC框架通常提供语言支持的接口,且可以处理更多复杂的功能,如负载均衡、服务发现、认证等。不同的RPC框架可能对跨语言支持有所不同。
HTTP:HTTP具有良好的跨平台兼容性,几乎所有的设备和编程语言都支持HTTP。它的扩展性较强,尤其在Web开发和REST API中,HTTP可以轻松支持新的功能和特性。

总结

RPC: 是一种允许客户端调用远程服务的协议,强调高效、低延迟的远程过程调用,适合分布式系统和微服务架构。
HTTP: 是一种基于请求-响应模型的协议,广泛应用于Web通信,适合传输数据,尤其是通过RESTful API进行服务交互。

何时选择RPC,何时选择HTTP:

选择RPC: 当你需要高效的、低延迟的远程调用,并且系统涉及多个服务之间的频繁通信时,RPC是一个很好的选择,尤其是在微服务架构下。
选择HTTP: 如果你的应用场景是Web应用、移动应用或者简单的服务请求/响应交互,且需要良好的跨平台支持和兼容性,那么HTTP是最常见且合适的选择。

2. RPC比Http的优势在哪里?

在微服务架构中,Spring Cloud 是一个广泛使用的框架,它提供了很多功能,如服务发现、负载均衡、配置管理等。而在微服务之间的通信方式上,RPC 和 HTTP 各有其优劣。虽然 HTTP 作为 RESTful API 在微服务架构中非常流行,但 RPC(尤其是 gRPC 或其他高效的二进制协议)在某些场景下具有显著的优势。
RPC 相比 HTTP 在 Spring Cloud 微服务中的优势

2.1 高效的二进制协议

RPC:使用如 gRPC 这样的框架,RPC 通信通常基于高效的二进制协议(如 Protobuf)。这比 HTTP 使用的文本协议(如 JSON)要高效得多。二进制协议具有更小的数据包、更低的序列化/反序列化开销和更少的网络带宽消耗。
性能优势:在高频调用、高并发的场景下,RPC 提供了比 HTTP 更高的性能。
延迟低:由于数据传输量小,RPC 请求和响应的延迟通常较低,适合要求低延迟的微服务通信。
HTTP:HTTP 使用的 JSON 格式进行数据交换,比起二进制格式(如 Protobuf)在序列化/反序列化和传输上效率较低。

2.2 支持双向流通信(Streaming)

RPC(如 gRPC):支持双向流(bidirectional streaming),允许客户端和服务器之间进行持续的、双向的数据流通信。这对于需要实时数据交换和高频交互的场景(如实时监控、聊天应用、事件驱动架构等)非常有用。
双向流优势:客户端和服务端可以在单个连接中持续发送和接收数据,无需每次都重新建立连接。
HTTP:传统的 HTTP 请求响应模式只支持单向通信。虽然 WebSocket 和 HTTP/2 可以部分支持流式通信,但它们的实现和维护相对复杂,且无法提供与 gRPC 一样的高效双向流支持。

2.3 自动化的代码生成与接口定义

RPC:在 gRPC 或其他 RPC 框架中,服务接口通过 Protobuf 等工具定义,然后通过代码生成器自动生成客户端和服务器的代码。这使得服务接口的定义和实现非常简洁,而且可以保证客户端和服务端始终一致。
开发体验:开发人员可以专注于业务逻辑,不需要手动维护 HTTP 请求和响应的细节,减少了编码错误和不一致性。
HTTP:虽然 Spring Cloud 也提供了方便的工具来处理 HTTP API(例如通过 Spring MVC 定义 RESTful API),但它不如 RPC 那样能够自动化生成客户端和服务器代码,且需要开发人员手动维护 API 定义和实现。

2.4 强类型接口和高效的错误处理

RPC:在 gRPC 中,接口的定义是强类型的,且支持自动化的错误码管理和错误处理。开发人员可以清晰地定义每个 RPC 方法的输入输出类型,同时也能通过 Protobuf 生成的错误代码进行规范化处理。
HTTP:虽然 HTTP 也可以通过返回状态码来进行错误处理(如 4xx 和 5xx 错误码),但是它没有像 gRPC 那样强类型的接口支持。错误处理通常需要开发人员手动解析响应体中的错误信息,并且 JSON 格式可能带来一定的处理开销。

2.5 更好的服务发现与负载均衡

RPC:很多 RPC 框架(如 gRPC)集成了与服务发现和负载均衡相关的功能。通过与 Spring Cloud Eureka、Consul 或其他服务发现机制结合,RPC 可以自动发现目标服务并实现高效的负载均衡。
HTTP:虽然 Spring Cloud 也提供了 HTTP 的服务发现和负载均衡机制(如 Ribbon),但在实现上,RPC 协议往往更加轻量和高效,特别是在跨服务调用的高并发场景中。

2.6 跨语言支持

RPC:gRPC 和其他 RPC 框架通常提供了多语言支持,这使得你可以用不同的编程语言实现微服务。例如,可以用 Java 实现一个服务端,其他服务端可以用 Python、Go 等语言实现。这使得系统的跨语言协作更容易实现。
HTTP:虽然 HTTP 也可以跨语言支持,但它的协议本身并没有 RPC 那样针对服务调用进行优化。因此,跨语言时可能面临更多的转换和处理工作,尤其是在数据格式上。

2.7 兼容性和版本管理

RPC:在 gRPC 和类似的 RPC 框架中,版本管理非常清晰。通过 Protobuf 的 消息版本控制,你可以在不破坏旧版本接口的情况下,轻松增加新字段或方法,确保服务的向前兼容性和向后兼容性。
HTTP:HTTP 的版本管理往往通过 URL 路径(如 /v1/ 和 /v2/)进行,但对于复杂的数据模型或变化较大的接口,可能需要更多的手动管理,尤其是处理不同客户端兼容性时。

2.8 内建安全性和认证支持

RPC:许多 RPC 框架(如 gRPC)内建对 TLS/SSL 加密的支持,以及对 认证和授权 的支持。你可以很方便地集成 OAuth2、JWT 等安全机制。
HTTP:虽然 HTTP 也支持通过 SSL/TLS 实现加密,但认证和授权机制(如 OAuth2、JWT)通常需要额外的配置和集成。Spring Security 为 HTTP 提供了强大的支持,但其配置和实现可能比 RPC 更复杂。

总结:RPC 在 Spring Cloud 微服务架构中的优势

性能:RPC 使用二进制协议,比 HTTP 更高效,适合高并发、高频调用的场景。
低延迟:RPC 提供更低的通信延迟,适合对实时性要求较高的服务间通信。
双向流和实时通信:RPC 可以提供双向流通信,适合需要实时数据交换的场景。
自动化接口生成:RPC 框架可以通过接口定义自动生成客户端和服务端代码,简化开发。
强类型接口:RPC 提供强类型接口,有助于提高代码的安全性和可维护性。
服务发现与负载均衡:RPC 与服务发现和负载均衡的集成非常高效。
跨语言支持:RPC 的框架通常支持多种编程语言,适合异构系统。

当然,选择 RPC 还是 HTTP 需要根据具体的应用场景来决定。RPC 在高性能和高效通信的场景下非常有优势,而 HTTP(尤其是 RESTful API)则在易用性、跨平台兼容性和开发速度上有较大的优势。如果你使用 Spring Cloud,可以根据具体需求选择最适合的通信协议。

3.为什么在这个社交平台使用的是微服务架构呢?有什么考量吗?

在小红书这样一个大型社交平台中使用 微服务架构,是出于一系列技术、业务和运营上的考量。微服务架构可以帮助像小红书这样的应用系统应对其复杂的业务需求、规模扩展、以及不断变化的用户需求。以下是一些主要的考量因素:

3.1 系统复杂性和功能模块化

小红书作为一个综合性的社交平台,提供了诸如 内容创作、社交互动、电商交易、个性化推荐 等多个业务模块。这些模块往往具有不同的业务逻辑、技术需求和发展方向,传统的单体架构(Monolithic Architecture)可能会变得非常臃肿和难以维护。
模块化管理:微服务架构能够将这些复杂的业务功能拆分成多个独立的小服务,每个服务负责一个具体的业务功能。这样做不仅能提升代码的可维护性和可扩展性,还能减少模块之间的耦合,使得团队可以并行开发。
独立部署和开发:每个微服务都可以独立开发、测试和部署,避免了单体架构中对整个系统进行重构和重新部署时的风险和复杂性。

3.2 灵活的技术栈选择

小红书作为一个快速发展的平台,可能需要应对多种技术需求。微服务架构允许不同的服务使用不同的技术栈和工具,根据具体的业务需求进行优化。
技术多样性:例如,电商模块可能需要处理大量并发交易,适合使用高并发、高性能的技术栈;而社交互动部分可能更侧重于实时性和高可用性,可能采用不同的技术栈或数据库来支持。
技术独立性:每个微服务能够独立升级和演进,技术栈的更新不会影响到其他服务,支持快速适应技术发展的变化。

3.3 高可扩展性和高可用性

随着用户规模的增长和业务的不断扩展,小红书需要有能力应对不断增加的 流量、数据量 和 请求量。微服务架构的分布式特性能够提供更好的扩展性和容错能力。
按需扩展:微服务架构可以根据每个服务的负载情况进行单独的扩展。例如,社交互动模块可能需要更频繁的扩展来处理大量的实时消息和通知,而推荐算法模块则可能在高流量期间进行扩展来处理更多的个性化推荐请求。
容错和高可用性:如果某个微服务出现问题,不会影响到整个系统的运行。微服务可以通过负载均衡、自动故障转移和分布式容错机制确保高可用性。

3.4. 快速迭代与持续交付

社交平台如小红书需要频繁地推出新功能、优化现有功能、修复 Bug,并且还要快速响应用户的需求变化。微服务架构有助于加快产品迭代和发布频率。
独立部署与更新:每个微服务可以独立部署和更新,避免了整体系统更新的复杂性。这样,开发团队可以对每个微服务进行持续集成和持续交付,缩短发布周期。
敏捷开发:微服务架构支持敏捷开发方式,开发团队可以更加灵活地进行业务功能开发、测试和发布,从而更快速地响应市场需求。

3.5 跨团队协作与 DevOps 支持

小红书这样的大型平台,开发团队通常是跨职能、跨部门的,需要紧密协作。微服务架构为跨团队合作提供了很好的支持。
团队自治:每个微服务可以由独立的团队负责,团队可以独立选择技术栈、开发方式和发布周期。这促进了团队的自治性,减少了开发的依赖关系。
DevOps 支持:微服务架构与 DevOps 文化高度契合,支持自动化部署、持续集成、持续交付等。通过自动化的流水线,开发、测试、运维团队可以更高效地协作,确保系统的稳定和高效运营。

3.6 大规模用户和流量处理

小红书是一个庞大的社交平台,每天都有大量的用户产生互动、发布内容、浏览商品等。微服务架构通过服务拆分和分布式部署,能够有效地应对 海量用户并发 和 大数据处理。
高并发处理:小红书需要处理大量用户的访问请求和数据交互。通过微服务架构,可以将流量分散到不同的服务上,通过负载均衡来平衡压力,确保系统在高并发下依然能保持流畅。
数据分片与分布式存储:微服务架构可以将不同模块的数据分布在不同的数据库或存储系统中,从而提高数据访问速度和系统的伸缩性。

3.7 数据隐私与合规性

随着互联网用户数据量的增加,数据隐私和合规性成为了越来越重要的问题。微服务架构可以帮助小红书更好地管理用户数据,并确保各个服务符合不同区域的数据保护法规(如 GDPR、CCPA 等)。
数据隔离与安全:不同服务的数据可以隔离管理,避免单一服务泄露用户信息。此外,每个微服务可以独立管理数据加密、认证和访问控制,确保数据安全性。
合规性管理:微服务架构允许小红书更容易地应对不同地区的合规要求,数据存储和处理可以根据不同地区的法规进行调整。

3.8 事件驱动与异步处理

社交平台通常有大量的 异步事件(如用户点赞、评论、推送通知等)需要处理。微服务架构非常适合与 事件驱动架构 配合,能够高效处理这些异步任务。
消息队列:小红书可以通过消息队列(如 Kafka、RabbitMQ 等)在不同微服务之间传递事件,确保系统的松耦合和高效处理。
解耦与异步:微服务架构支持将一些长时间运行的任务(如视频处理、数据分析等)异步化,从而避免阻塞主线程,提高系统响应速度。

总结

小红书选择微服务架构的主要考量因素包括:
系统复杂性和模块化管理:通过微服务将不同业务模块分离,简化开发和维护。
高扩展性和高可用性:能够有效应对大量用户和流量的挑战,支持独立扩展和容错。
技术多样性:根据不同业务需求,灵活选择技术栈,优化系统性能。
快速迭代与持续交付:支持快速的产品更新和功能迭代,缩短开发周期。
跨团队协作与 DevOps:提高跨部门协作效率,支持自动化部署和持续集成。
数据隐私与合规性:通过数据隔离和安全控制,满足数据保护要求。

因此,微服务架构为小红书提供了 灵活性、可扩展性 和 高效的运维管理,帮助其在快速发展的过程中应对技术挑战,并确保平台的稳定性和用户体验。

4. 讲解一下redis当中的布隆过滤器

那问题来了,布隆过滤器是如何工作的呢?接下来,我介绍下。

布隆过滤器由「初始值都为 0 的位图数组」和「 N 个哈希函数」两部分组成。我们在写入数据库数据时,在布隆过滤器里做个标记,这样下次查询数据是否在数据库时,只需要查询布隆过滤器,如果查询到数据没有被标记,说明不在数据库中。
布隆过滤器会通过 3 个操作完成标记:

  • 第一步,使用 N 个哈希函数分别对数据做哈希计算,得到 N 个哈希值;
  • 第二步,将第一步得到的 N 个哈希值对位图数组的长度取模,得到每个哈希值在位图数组的对应位置。
  • 第三步,将每个哈希值在位图数组的对应位置的值设置为 1;
    在数据库写入数据 x 后,把数据 x 标记在布隆过滤器时,数据 x 会被 3 个哈希函数分别计算出 3 个哈希值,然后在对这 3 个哈希值对 8 取模,假设取模的结果为 1、4、6,然后把位图数组的第 1、4、6 位置的值设置为 1。当应用要查询数据 x 是否数据库时,通过布隆过滤器只要查到位图数组的第 1、4、6 位置的值是否全为 1,只要有一个为 0,就认为数据 x 不在数据库中。
    布隆过滤器由于是基于哈希函数实现查找的,高效查找的同时存在哈希冲突的可能性,比如数据 x 和数据 y 可能都落在第 1、4、6 位置,而事实上,可能数据库中并不存在数据 y,存在误判的情况。
    所以,查询布隆过滤器说数据存在,并不一定证明数据库中存在这个数据,但是查询到数据不存在,数据库中一定就不存在这个数据。

4.1 讲解一下在点赞过程中使用布隆过滤器是干什么呢?

笔记点赞流程设计为:
在这里插入图片描述
笔记点赞接口的处理流程如下:

  • 判断被点赞的笔记是否真实存在,若不存在,则返回提示信息:笔记不存在;
  • 判断笔记是否被点赞过,若已点赞,则返回提示信息:该篇笔记已点赞;
  • 判断用户的 ZSET 点赞列表是否存在,若存在,再判断 ZSET 的大小已经达到 100(每页10条,共10页的数据):
    • 若达到,则向 ZSET 中添加最新的点赞笔记,并将点赞时间最久的那篇,移除出 ZSET;
    • 若未达到,则直接向 ZSET 中添加最新的点赞笔记;
  • 发送 MQ, 让消费者去将点赞记录存入 t_note_like 表中;

4.2 在点赞这个用户行为中为什么呢要使用布隆过滤器

  • 实现原理: 由于 Redis Set 在存储大量用户数据时可能导致较高的内存消耗,使用 布隆过滤器(Bloom Filter)可以有效地减少存储空间。布隆过滤器可以用来快速判断一个用户是否可能已经点赞过该笔记,但可能会存在少量误报(false positive)。
  • 操作步骤:
  1. 为每个用户点赞列表初始化一个布隆过滤器,并在每次点赞笔记时,将笔记 ID 入到该布隆过滤器中;
  2. 使用 Bloom Filter 的 BF.EXISTS 命令判断笔记是否已经点赞过;
  3. 如果返回未点赞(不存在误报),则继续执行后续点赞逻辑;若返回已点赞(可能有误报),则需要进一步确认是否已点赞。
  • 优点:显著减少内存消耗,适合海量用户场景。
  • 缺点:对于已点赞笔记,存在一定的误判(对未点赞的笔记判断,则绝对正确)。

5.使用 Redis ZSET + MQ 异步落库怎样做的,目的是什么

5.1 为什么使用Zset

首先 Zset可以按照某种顺序进行排列
在点赞这个用户行为中,执行Lua脚本,并使用 Zset 对数据进行添加,若点赞数据已经存在,那么 Zset会按照点赞数目进行排序。

5.2 Zset底层是如何实现的

Zset 类型的底层数据结构是由压缩列表或跳表实现的:

  • 如果有序集合的元素个数小于 128 个,并且每个元素的值小于 64 字节时,Redis 会使用压缩列表作为 Zset 类型的底层数据结构;
  • 如果有序集合的元素不满足上面的条件,Redis 会使用跳表作为 Zset 类型的底层数据结构;
    在 Redis 7.0 中,压缩列表数据结构已经废弃了,交由 listpack 数据结构来实现了。

5.3 跳表是怎么实现的?

链表在查找元素的时候,因为需要逐一查找,所以查询效率非常低,时间复杂度是O(N),于是就出现了跳表。跳表是在链表基础上改进过来的,实现了一种「多层」的有序链表,这样的好处是能快读定位数据。
那跳表长什么样呢?我这里举个例子,下图展示了一个层级为 3 的跳表。
在这里插入图片描述
图中头节点有 L0~L2 三个头指针,分别指向了不同层级的节点,然后每个层级的节点都通过指针连接起来:

  • L0 层级共有 5 个节点,分别是节点1、2、3、4、5;
  • L1 层级共有 3 个节点,分别是节点 2、3、5;
  • L2 层级只有 1 个节点,也就是节点 3 。
    如果我们要在链表中查找节点 4 这个元素,只能从头开始遍历链表,需要查找 4 次,而使用了跳表后,只需要查找 2 次就能定位到节点 4,因为可以在头节点直接从 L2 层级跳到节点 3,然后再往前遍历找到节点 4。
    可以看到,这个查找过程就是在多个层级上跳来跳去,最后定位到元素。当数据量很大时,跳表的查找复杂度就是 O(logN)。
    那跳表节点是怎么实现多层级的呢?这就需要看「跳表节点」的数据结构了,如下:

详情见小林Coding Redis章节 : 跳表是怎么实现的?

6.Redis中的IO复用了解过吗?

解析见:Redis怎么实现的io多路复用?

7. Java 中的 Set 结构:底层实现、常见问题及解决方案

7.1. Set 的主要实现类及其底层实现

7.1.1 HashSet

  • 底层实现
    HashSet 是基于 HashMap 实现的。具体来说,HashSet 内部使用一个 HashMap 来存储元素,其中每个元素作为 HashMap 的 key,而 value 则是一个固定值(通常是一个占位符对象)。
  • 特点
    • 元素的存储顺序不保证。
    • 使用哈希表存储元素,依赖元素的 hashCode()equals() 方法来判断元素是否重复。
    • 如果发生哈希冲突,HashSet 使用链表或红黑树解决冲突(从 Java 8 开始,当冲突链表长度超过阈值时,会转为红黑树)。
  • 时间复杂度
    • 插入、删除、查找:平均 O(1),最坏情况(哈希冲突严重):O(n)

7.1.2 TreeSet

  • 底层实现
    TreeSet 是基于 TreeMap 实现的,其底层是一个红黑树。
  • 特点
    • 元素会按自然顺序(或通过提供的比较器)排序。
    • 判断重复依赖于 compareTo() 或比较器的 compare() 方法,而不是 hashCode()equals()
  • 时间复杂度
    • 插入、删除、查找:O(log n),因为红黑树的高度是对数级别。

7.1.3 LinkedHashSet

  • 底层实现
    LinkedHashSet 是基于 HashMap 和双向链表实现的,它继承了 HashSet 的特点,并通过双向链表维护元素的插入顺序。
  • 特点
    • 保证了元素的迭代顺序与插入顺序一致。
    • 其判断重复的方式与 HashSet 相同(依赖 hashCode()equals())。
  • 时间复杂度
    • 插入、删除、查找:O(1),与 HashSet 类似,但由于维护了插入顺序,实际开销稍大。

7.2. Set 常见问题及解决方案

7.2.1 重复元素判断问题

  • 问题
    对于 HashSetLinkedHashSet,重复元素的判断依赖于 hashCode()equals() 方法。如果这两个方法未正确实现(例如,对于自定义对象,没有重写 hashCode()equals()),则可能导致重复元素无法被正确识别。
  • 解决方法
    • 为自定义对象重写 hashCode()equals() 方法。
    • hashCode() 的实现应尽量分布均匀,以减少哈希冲突。
    • equals() 的实现需要满足自反性、对称性、传递性和一致性。

7.2.2 哈希冲突问题

  • 问题
    HashSet 中,如果多个元素的 hashCode() 值相同(发生哈希冲突),它们会被存储在同一个桶中,影响性能。
  • 解决方法
    • 重写 hashCode() 方法,确保哈希值分布均匀。
    • 从 Java 8 开始,HashSet 在哈希冲突时会使用红黑树优化性能(当冲突链表长度超过 8 时),降低最坏情况的时间复杂度。

7.2.3 顺序问题

  • 问题
    HashSet 中,元素的存储顺序是不可预测的,因此如果需要有序存储(例如按插入顺序或按某种比较规则),HashSet 不适用。
  • 解决方法
    • 如果需要按插入顺序存储,使用 LinkedHashSet
    • 如果需要按自然顺序或自定义顺序存储,使用 TreeSet

7.2.4 性能问题

  • 问题
    在高并发场景下,HashSetTreeSet 是线程不安全的,可能会导致数据不一致或异常。
  • 解决方法
    • 使用 Collections.synchronizedSet() 方法来包装 HashSetTreeSet,使其线程安全。
    • 或者,使用 ConcurrentSkipListSet,它是线程安全的,底层基于跳表实现,适合并发场景。

7.3. Set 时间复杂度总结

操作HashSetTreeSetLinkedHashSet
插入元素平均 O(1)O(log n)平均 O(1)
删除元素平均 O(1)O(log n)平均 O(1)
查找元素平均 O(1)O(log n)平均 O(1)
遍历所有元素O(n)O(n)(按顺序)O(n)(按插入顺序)

7.4. 总结和适用场景

  • HashSet

    • 优点:快速访问(O(1))。
    • 缺点:不保证顺序。
    • 适用场景:需要快速去重的集合操作,且对顺序无要求。
  • TreeSet

    • 优点:有序集合(按自然顺序或自定义比较器)。
    • 缺点:性能略低于 HashSet,插入和删除操作需要维护红黑树(O(log n))。
    • 适用场景:需要有序存储或快速范围查询的场景。
  • LinkedHashSet

    • 优点:既有 HashSet 的高性能,又能维护插入顺序。
    • 缺点:比 HashSet 占用更多的内存。
    • 适用场景:需要快速访问且需要维护插入顺序的场景。

根据实际需求选择合适的 Set 实现,正确设计 hashCode()equals() 方法,并在必要时优化性能(如减少哈希冲突),即可充分发挥 Set 的优势。

8. 线程池情景题:目前有10个线程,线程1还未执行完,线程2-10已经到达。核心线程数是3,最大线程数是6,组合队列是3。那么从线程1到线程10是怎样一个执行过程?

组合队列的作用

线程池 中,组合队列(也就是任务队列)的作用非常重要。队列的主要功能是:

  1. 缓存等待执行的任务:如果线程池中的线程数已经达到最大线程数,接下来的任务会被放入队列中,等待空闲线程来执行。
  2. 控制并发:队列容量的限制有助于限制并发任务的数量,从而防止线程池创建过多线程,过度消耗系统资源。

组合队列在这个情境中的作用

根据题目,线程池的配置如下:

  • 核心线程数:3
  • 最大线程数:6
  • 队列容量(组合队列):3

这意味着:

  • 线程池可以同时运行 3个核心线程,并且如果核心线程有空闲,它会自动创建新线程,直到达到 最大线程数(6个线程)
  • 一旦达到最大线程数,后续的任务会进入 队列(最多能容纳 3个任务)。

具体执行过程中的队列作用

1. 线程池的初期执行(线程1到线程3)

  • 线程1开始执行(核心线程1)。
  • 随后,线程2线程3到达,线程池会分配这两个任务到 空闲的核心线程2和线程3
  • 此时线程池中的 3个核心线程(线程1、线程2、线程3)都在工作,队列为空。

2. 新任务到达,线程池达到最大线程数(线程4到线程6)

  • 线程4、线程5、线程6到达。此时,线程池已经有 3个核心线程(线程1、线程2、线程3)在工作,且核心线程数已满。由于线程池的 最大线程数为6,线程池会启动新的线程来执行任务。
  • 所以,线程池会分配 线程4线程5线程6 到新创建的线程,线程池现在有 6个线程在工作(线程1到线程6)。

3. 队列的作用(线程7到线程9)

  • 线程7、线程8、线程9到达。此时,线程池已经有 6个线程在运行,达到了最大线程数,线程池无法再创建新的线程来处理任务。
  • 由于队列容量为 3线程7、线程8、线程9 将被 放入队列中,等待空闲的线程来执行。
  • 这时候,线程池内部的情况是:6个线程(线程1到线程6)在执行任务,且 队列中有3个任务(线程7到线程9),等待执行。

4. 空闲线程回收和队列中任务的执行

  • 线程1、线程2和线程3先后完成。由于线程池的 核心线程数为3,这些线程完成后会保持空闲,等待新的任务。如果任务队列中有待执行的任务,它们会被分配给空闲的线程。
  • 于是,线程7 会被分配给空闲的线程1(假设线程1空闲),然后 线程8 会被分配给线程2,依此类推,直到队列中的任务(线程7、线程8、线程9)都被执行完。

5. 线程10的执行

  • 线程4、线程5、线程6 完成后,线程池就会从队列中取出 线程10 来执行,直到所有任务完成。

关键点总结

  • 组合队列 在这个情景中的作用是 缓存 待执行的任务。当线程池中的活跃线程数达到最大线程数(6个线程)时,后续到达的任务会被放入队列中,等待有空闲线程时再执行。
  • 队列的容量限制了队列中最多能有多少个任务(在本题中为3个),这是防止系统过载的一个重要机制。
  • 队列的容量线程池的最大线程数共同决定了线程池的并发执行能力。队列满时,任务会被阻塞,直到有空闲线程可以继续执行队列中的任务。

总结:

  • 线程池会优先使用核心线程数来执行任务,直到达到最大线程数。
  • 一旦线程池达到最大线程数,后续的任务会被放入队列(最多3个任务),等待线程池中的线程空闲后才会继续执行队列中的任务。
  • 队列的作用是缓存等待的任务并避免过多线程的创建,它保证了线程池的任务管理与资源使用之间的平衡。

9. 在MySQL中B树和B+树的区别以及B+ 树的优势

详情请见如下回答:说说B+树和B树的区别


http://www.ppmy.cn/news/1561523.html

相关文章

单片机-串转并-74HC595芯片

1、74HC595芯片介绍 74HC595 是一个 8 位串行输入、并行输出的位移缓存器,其中并行输出为三态输出(即高电平、低电平和高阻抗)。 15 和 1 到 7 脚 QA--QH:并行数据输出 9 脚 QH 非:串行数据输出 10 脚 SCLK 非&#x…

第12章 数据库其它调优策略

第12章 数据库其它调优策略 1. 数据库调优的措施 1.1 调优的目标 尽可能节省系统资源,以便系统可以提供更大负荷的服务。(吞吐量更大)合理的结构设计和参数调整,以提高用户操 响应的速度。(响应速度更快&#xff09…

javascript-闭包

REF:https://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html

设计模式 结构型 组合模式(Composite Pattern)与 常见技术框架应用 解析

组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次结构。通过这种模式,客户端可以一致地处理单个对象和对象组合。 在软件开发中,我们经常会遇到处理对象的层…

单片机实现模式转换

[任务] 要求通过单片机实现以下功能: 1.单片机有三种工作模式(定义全局变量MM表示模式,MM1,2,3表示三种不同的模式) LED控制模式 风扇控制模式 蜂鸣器控制模式 2.可以在某一个模式下通过拓展板KEY1按键控制设备 (按…

imageio 图片转mp4 保存mp4

目录 安装: imageio 图片转mp4 numpy 保存mp4 安装: FFMPEG: pip install imageio[ffmpeg] pyav: pip install imageio[pyav] imageio 图片转mp4 import glob import osimport cv2 import imageio from natsort import natsortedfrom PIL import …

A/B实验之置信检验(一):如何避免误判 (I类) 和漏报 (II类)

假设检验的依据:如何避免误判和漏报 A/B实验系列相关文章(置顶) 1.A/B实验之置信检验(一):如何避免误判和漏报 2.A/B实验之置信检验(二):置信检验精要 引言 在数据驱动…

Ungoogled Chromium127 编译指南 MacOS篇(四)- 安装Homebrew和Git

1. 引言 在 Ungoogled Chromium 的编译过程中,包管理器和版本控制系统是不可或缺的工具。Homebrew 作为 macOS 最流行的包管理器,将帮助我们轻松安装和管理各种依赖;而 Git 则负责代码的获取和版本控制。本文将详细介绍如何在 macOS 上安装和…