(转)如何保障微服务架构下的数据一致性?

转自: https://cloud.tencent.com/developer/article/1459734

【1】写在前面

随着微服务架构的推广,越来越多的公司采用微服务架构来构建自己的业务平台。就像前边的文章说的,微服务架构为业务开发带来了诸多好处的同时,例如单一职责、独立开发部署、功能复用和系统容错等等,也带来一些问题。

例如上手难度变大,运维变得更复杂,模块之间的依赖关系更复杂,数据一致性难以保证,等等。但是办法总是比问题多,本篇文章就来介绍一下我们是如何保障微服务架构的数据一致性的


【2】微服务架构的数据一致性问题

以电商平台为例,当用户下单并支付后,系统需要修改订单的状态并且增加用户积分。由于系统采用的是微服务架构,分离出了支付服务、订单服务和积分服务,每个服务都有独立数据库做数据存储。当用户支付成功后,无论是修改订单状态失败还是增加积分失败,都会造成数据的不一致。

 为了解决例子中的数据一致性问题,一个最直接的办法就是考虑数据的 强一致性。那么如何保证数据的强一致性呢?我们从关系型数据库的 ACID 理论说起

ACID

关系型数据库具有解决复杂事务场景的能力,关系型数据库的事务满足 ACID 的特性。

  • Atomicity:原子性(要么都做,要么都不做)
  • Consistency:一致性(数据库只有一个状态,不存在未确定状态)
  • Isolation:隔离性(事务之间互不干扰)
  • Durability:永久性(事务一旦提交,数据库记录永久不变)

具有 ACID 特性的数据库支持数据的强一致性,保证了数据本身不会出现不一致。

然而微服务架构下,每个微服务都有自己的数据库,导致微服务架构的系统不能简单地满足 ACID,我们就需要寻找微服务架构下的数据一致性解决方案。

微服务架构的系统本身是一种分布式系统,而本文讨论的问题其实也就是分布式事务之数据一致性的问题,我们来聊聊分布式系统的 CAP 理论和 BASE 理论。

方案1:CAP

CAP 是指在一个分布式系统下, 包含三个要素:Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性),并且三者不可得兼

  • C:Consistency,一致性,所有数据变动都是同步的。
  • A:Availability,可用性,即在可以接受的时间范围内正确地响应用户请求。
  • P:Partition tolerance,分区容错性,即某节点或网络分区故障时,系统仍能够提供满足一致性和可用性的服务。

关系型数据库单节点保证了数据强一致性(C)和可用性(A),但是却无法保证分区容错性(P)。

然而在分布式系统下,为了保证模块的分区容错性(P),只能在数据强一致性(C)和可用性(A)之间做平衡。牺牲一致性或可用性。

具体表现为在一定时间内,可能模块之间数据是不一致的,但是通过自动或手动补偿后能够达到最终的一致(无法保证数据实时性一致,可以保证延时性一致或最终一致

方案2:BASE

BASE 理论主要是解决 CAP 理论中分布式系统的可用性和一致性不可兼得的问题。BASE 理论包含以下三个要素:

  • BA:Basically Available,基本可用。
  • S:Soft State,软状态,状态可以有一段时间不同步。
  • E:Eventually Consistent,最终一致,最终数据是一致的就可以了,而不是时时保持强一致

BASE 模型与 ACID 不同,满足 CAP 理论,通过牺牲强一致性来保证系统可用性。由于牺牲了强一致性,系统在处理请求的过程中,数据可以存在短时的不一致因为可以手动或自动补偿或重试)。

系统在处理业务时,记录每一步的临时状态。当出现异常时,根据状态判断是否继续处理请求或者退回原始状态,从而达到数据的最终一致。

例如,在上面的案例中,支付成功,订单也成功,但增加积分失败,此时,不应回滚支付和订单,而应通过一些补偿方法来让积分得以正确地增加。后面会讲到具体的实现方法。

在分享我们的分布式事务实践方案之前,先看看早期解决分布式事务问题的二阶段提交协议


【3】二阶段提交协议

X/Open DTP(Distributed Transaction Process)是一个分布式事务模型,此模型主要使用二阶段提交(2PC,Two-Phase-Commit)来保证分布式事务的完整性。在这个模型里面,二阶段提交协议有三个角色

  • AP:Application,应用程序,业务层。
  • RM:Resource Manager,资源管理器,关系型数据库或支持 XA 接口(XA 规范是 X/Open 组织定义的分布式事务规范)的组件。
  • TM:Transaction Manager ,事务管理器,负责各个 RM 的提交和回滚。

当应用程序(AP)调用了事务管理器(TM)的提交方法时,事务的提交分为两个阶段实行。

第一阶段(准备阶段)

TM 通知所有参与事务的各个 RM,给每个 RM 发送 prepare 消息。

RM 接收到消息后进入准备阶段后,要么直接返回失败,要么创建并执行本地事务,写本地事务日志(redo 和 undo 日志),但是不提交(此处只保留最后一步耗时最少的提交操作给第二阶段执行)

我的理解为:TM 调用 RM,TM线程阻塞;RM开启事务,处理数据写入db,但是不提交事务;RM向TM发送请求,说我已经完成,RM线程阻塞,等待TM的回复

第二阶段(提交 / 回滚阶段)

TM 收到 RM 准备阶段的失败消息或者获取 RM 返回消息超时,则TM直接给 RM 发送回滚(rollback)消息,否则TM发送提交(commit)消息给RM

RM 根据 TM 的指令执行提交或者回滚,执行完成后释放所有事务处理过程中使用的锁(最后阶段释放锁)。

2pc(2-phrase commit)两阶段提交协议中,要理解到:

1)第一阶段中,TM与RM在通信过程中,两者线程均是阻塞状态,即RM没有提交事务;

2)第二阶段中,RM提交事务,发送OK到TM后, 线程才算执行完成;

二阶段提交的利弊

优点

2PC 提供了一套完整的分布式事务的解决方案,遵循事务严格的 ACID 特性。

缺点

  • 缺点1,db并发性低,数据被锁定跨越整个流程:TM 通过 XA 接口与各个 RM 之间进行数据交互,从第一阶段的准备阶段,业务所涉及的数据就被锁定,并且锁定跨越整个提交流程(db并发性能低)。在高并发和涉及业务模块较多的情况下对数据库的性能影响较大。
  • 缺点2,模块多TM,RM,扩展性低:二阶段是反可伸缩模式的,业务规模越大,涉及模块越多,局限性越大,系统可伸缩性越差(涉及模块多如TM,RM,故扩展性低)。
  • 缺点3,不是所有组件都支持XA协议:在技术栈比较杂的分布式应用中,存储组件有很多不支持 XA 协议(各个服务间可能不属于同一个技术框架,封装的网络协议不同)

二阶段的诸多弊端,导致分布式系统下无法直接使用此方案来解决数据一致性问题,但它提供了解决分布式系统下数据一致性问题的思路


【4】微服务数据同步一致性案例

下面就通过案例来分享我们是如何保证微服务架构的数据一致性的。

方案1——可靠消息最终一致性(微服务组件技术栈不同时使用)

可靠消息最终一致性方案本质上是利用 MQ 组件实现的二阶段提交。此方案涉及 3 个模块:

  • 上游应用,执行业务并发送 MQ 消息。
  • 可靠消息服务和 MQ 消息组件,协调上下游消息的传递,并确保上下游数据的一致性。
  • 下游应用,监听 MQ 的消息并执行自身业务。

上游应用执行业务并发送 MQ 消息(第一阶段)

上游应用将本地业务执行和消息发送绑定在同一个本地事务中,保证要么本地操作成功并发送 MQ 消息,要么两步操作都失败并回滚。

上游应用和可靠消息之间的业务交互图如下:

  1. 上游应用发送待确认消息到可靠消息系统
  2. 可靠消息系统保存待确认消息并返回
  3. 上游应用执行本地业务
  4. 上游应用通知可靠消息系统确认业务已执行并发送消息。
  5. 可靠消息系统修改消息状态为发送状态并将消息投递到 MQ 中间件。

以上每一步都可能出现失败情况,分析一下这 5 步出现异常后上游业务和消息发送是否一致:

失败步骤现象一致性
第1步上游应用未执行,mq消息未发送;一致
第2步上游应用未执行,mq消息未发送;一致
第3步上游应用回滚,mq消息未发送;一致
第4步上游应用执行,mq消息未发送;不一致
第5步上游应用执行,mq消息未发送;不一致

上游应用执行完成,下游应用尚未执行或执行失败时,此事务即处于 BASE 理论的 Soft State 状态。

下游应用监听 MQ 消息并执行业务(第二阶段)

下游应用监听 MQ 消息并执行业务,并且将消息的消费结果通知可靠消息服务。

可靠消息的状态需要和下游应用的业务执行保持一致,可靠消息状态不是已完成时,确保下游应用未执行,可靠消息状态是已完成时,确保下游应用已执行。

下游应用和可靠消息服务之间的交互图如下:

  1. 下游应用监听 MQ 消息组件并获取消息
  2. 下游应用根据 MQ 消息体信息处理本地业务
  3. 下游应用向 MQ 组件自动发送 ACK 确认消息被消费
  4. 下游应用通知可靠消息系统消息被成功消费,可靠消息将该消息状态更改为已完成

以上每一步都可能出现失败情况,分析一下这 4 步出现异常后下游业务和消息状态是否一致:

失败步骤现象一致性
第1步下游应用未执行,消息状态为已发送;一致
第2步下游应用回滚,消息状态为已发送;一致
第3步下游应用回滚,消息状态为已发送;一致
第4步下游应用已执行,消息状态为已发送;不一致

通过分析以上两个阶段可能失败的情况,为了确保上下游数据的最终一致性,在可靠消息系统中,需要开发  消息状态确认和消息重发  两个功能以实现 BASE 理论的 Eventually Consistent 特性

补充:可靠性消息数据一致性方案,上游+可靠消息服务+MQ+下游整体时序图(汇总)

可靠消息一致性方案补偿机制1:消息状态确认

可靠消息服务定时监听待确认消息(通过调度线程或定时任务实现),如果存在状态为待确认并且超时的消息,则表示上游应用和可靠消息交互中的步骤 4 或者 5 出现异常。

可靠消息则携带消息体内的信息向上游应用发起请求查询该业务是否已执行。上游应用提供一个可查询接口供可靠消息追溯业务执行状态,如果业务执行成功则更改消息状态为已发送,否则删除此消息确保数据一致。具体流程如下:

  1. 可靠消息查询超时的待确认状态的消息
  2. 向上游应用查询业务执行的情况
  3. 业务未执行,则删除该消息,保证业务和可靠消息服务的一致性。业务已执行,则修改消息状态为已发送,并发送消息到 MQ 组件。

可靠消息一致性方案补偿2:消息重发

消息已发送则表示上游应用已经执行,接下来则确保下游应用也能正常执行

可靠消息服务发现可靠消息服务中存在消息状态为已发送并且超时的消息,则表示可靠消息服务和下游应用中存在异常的步骤,无论哪个步骤出现异常,可靠消息服务都将此消息重新投递(重发)到 MQ 组件中供下游应用监听

下游应用监听到此消息后,在保证幂等性的情况下重新执行业务并通知可靠消息服务此消息已经成功消费,最终确保上游应用、下游应用的数据最终一致性。具体流程如下:

  1. 可靠消息服务定时查询状态为已发送并超时的消息
  2. 可靠消息将消息重新投递到 MQ 组件中
  3. 下游应用监听消息,在满足幂等性的条件下,重新执行业务
  4. 下游应用通知可靠消息服务该消息已经成功消费。

通过消息确认和消息重发两个功能,可以确保上游应用、可靠消息服务和下游应用数据的最终一致性。补充——消息确认保证了上游应用成功执行业务,消息重发保证了下游应用成功消费数据

当然在实际接入过程中,需要引入人工干预功能。比如引入重发次数限制,超过重发次数限制的将消息修改为死亡消息,等待人工干预。

回到业务场景: 代入开篇案例,通过可靠消息最终一致性方案,

第一阶段,订单状态更改之前,订单服务向可靠消息服务请求保存待确认消息(消息状态为待确认。可靠消息服务保存消息并返回。订单服务接收到返回信息后执行本地业务并通知可靠消息服务业务已执行。消息服务更改消息状态并将消息投递到 MQ 中间件(消息状态为已发送)。

第二阶段,积分系统监听到 MQ 消息,查看积分是否已增加,如果没有增加则修改积分,然后请求可靠消息服务。可靠消息服务接收到积分系统的请求,将消息状态更改为已完成(消息状态为已完成)。

到这里,已经介绍完如何通过可靠消息服务来保证数据的一致性。但由于引入了可靠消息服务和消息队列,带来了一定的复杂性,所以,它更适用于跨平台技术栈不统一的场景

下面再来介绍在技术栈统一的情况下,如何通过 TCC 来解决数据一致的方法。


【5】方案2——TCC(Try-Confirm-Cancel)(微服务组件技术栈相同时使用

TCC 方案是二阶段提交的另一种实现方式,它涉及 3 个模块,主业务、从业务和活动管理器(协作者)。

下面这张图是互联网上关于 TCC 比较经典的图示:

第一阶段:主业务服务分别调用所有从业务服务的 try 操作,并在活动管理器中记录所有从业务服务。当所有从业务服务 try 成功或者某个从业务服务 try 失败时,进入第二阶段

第二阶段:活动管理器根据第一阶段从业务服务的 try 结果来执行 confirm 或 cancel 操作。如果第一阶段所有从业务服务都 try 成功,则协作者调用所有从业务服务的 confirm 操作,否则,调用所有从业务服务的 cancel 操作。

在第二阶段中,confirm 和 cancel 同样存在失败情况,所以需要对这两种情况做 异常处理 以保证数据一致性

  • Confirm 失败:则回滚所有 confirm 操作并执行 cancel 操作。
  • Cancel 失败:从业务服务需要提供自动 cancel 机制,以保证 cancel 成功

目前有很多基于 RPC 的 TCC 框架,但是不适用于微服务架构下基于 HTTP 协议的交互模式。我们这次只讨论基于 HTTP 协议的 TCC 实现。具体的实现流程如下:

  1. 主业务服务调用从业务服务的 try 操作,并获取 confirm/cancel 接口和超时时间。
  2. 如果从业务都 try 成功,主业务服务执行本地业务,并将获取的 confirm/cancel 接口发送给活动管理器,活动管理器会顺序调用从业务 1 和从业务 2 的 confirm 接口并记录请求状态,如果请求成功,则通知主业务服务提交本地事务。如果 confirm 部分失败,则活动管理器会顺序调用从业务 1 和从业务 2 的 cancel 接口来取消 try 的操作。
  3. 如果从业务部分或全部 try 失败,则主业务直接回滚并结束,而 try 成功的从业务服务则通过定时任务来处理处于 try 完成但超时的数据,将这些数据做回滚处理保证主业务服务和从业务服务的数据一致。

代入开篇提到的案例,通过 TCC 方案,订单服务在订单状态修改之前执行预增积分操作(try),并从积分服务获取 confirm/cancel 预增积分的请求地址。

如果预增积分(try)成功,则订单服务更改订单状态并通知活动管理器,活动管理器请求积分模块的 confirm 接口来增加积分。

如果预增积分(try)失败,则订单服务业务回滚。积分服务通过定时任务删除预增积分(try)超时的数据。

另外如果活动管理器调用积分服务的 confirm 接口失败,则活动管理器调用积分服务 cancel 接口来取消预增积分,从而,保证订单和积分数据的最终一致性。

通过上面的对可靠消息服务和 TCC 方案的描述,我们解决了技术栈一致和不一致的两种情况下的数据一致性问题。

但是,通常在这些核心业务上有很多附加业务,比如当用户支付完成后,需要通过短信通知用户支付成功。

这一类业务的成功或者失败不会影响核心业务,甚至很多大型互联网平台在并高并发的情况下会主动关闭这一类业务以保证核心业务的顺利执行。那么怎么处理这类情况呢,我们来看看最大努力通知方案。


【6】方案三——最大努力通知(数据一致性要求低)

最大努力通知方案涉及三个模块:

  • 上游应用,发消息到 MQ 队列。
  • 下游应用(例如短信服务、邮件服务),接受请求,并返回通知结果。
  • 最大努力通知服务,监听消息队列,将消息存储到数据库中,并按照通知规则调用下游应用的发送通知接口。

具体流程如下:

  1. 上游应用发送 MQ 消息到 MQ 组件内,消息内包含通知规则和通知地址
  2. 最大努力通知服务监听到 MQ 内的消息,解析通知规则并放入延时队列等待触发通知
  3. 最大努力通知服务调用下游的通知地址,如果调用成功,则该消息标记为通知成功,如果失败则在满足通知规则(例如 5 分钟发一次,共发送 10 次)的情况下重新放入延时队列等待下次触发。

最大努力通知服务表示在不影响主业务的情况下,尽可能地确保数据的一致性。它需要开发人员根据业务来指定通知规则,在满足通知规则的前提下,尽可能的确保数据的一致,以尽到最大努力的目的。

根据不同的业务可以定制不同的通知规则(通知频率和最大通知次数),比如通知支付结果等相对严谨的业务,可以将通知频率设置高一些,通知时间长一些,比如隔 5 分钟通知一次,持续时间 1 小时。

如果不重要的业务,比如通知用户积分增加,则可以将通知频率设置低一些,时间短一些,比如 10 分钟通知一次,持续 30 分钟。

代入上面提到的支付成功短信通知用户的案例,通过最大努力通知方案,当支付成功后,将消息发送到 MQ 中间件,在消息中,定义发送规则为 5 分钟一次,最大发送数为 10 次

最大努力通知服务监听 MQ 消息并根据规则调用消息通知服务(短信服务)的消息发送接口,并记录每次调用的日志信息。在通知成功或者已通知 10 次时,停止通知。


总 * 结

上面通过案例详细介绍了我们解决微服务之间数据不一致问题的三种方案,下面通过一张简单的对比图,为大家选择合适的解决方案提供简单依据。

分布式事务方案对比
方案一致性复杂程度适用范围
可靠消息最终一致性跨平台业务,对数据一致性要求高
TCC-try-confirm-cancel最终一致性平台内容,技术栈一致,对数据一致性要求高
最大努力通知最终一致性附属功能,对一致性要求低;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/330021.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python中math库_Python的math库、random库实际应用

昨天在说那个列表的时候,我挖了一个坑,不知道你们看出来没有?就是用循环语句写迭代的时候,总是运行不了结果,其实是因为我没有缩进的问题,因为有一个for循环,下面print如果没有对应的缩进&#…

(转)漫画:什么是分布式事务?

转自: https://blog.csdn.net/bjweimengshu/article/details/79607522 假如没有分布式事务 在一系列微服务系统当中,假如不存在分布式事务,会发生什么呢?让我们以互联网中常用的交易业务为例子: 上图中包含了库存和订…

Java 线程池框架核心代码分析

转载自 Java 线程池框架核心代码分析前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和资源消耗都是很高的。线程池应运而生,成为我们管理线程的利器。Java 通过Executor接口,提供了一种标准的方法将任务的提…

python渐变色代码_如何在Python中创建颜色渐变?

6 个答案: 答案 0 :(得分:54) 我还没有看到一个简单的答案就是使用colour package。 通过pip安装 pip install colour 如此使用: from colour import Color red Color("red") colors list(red.range_to(Color("green"),10)) # col…

(转)web.xml 中的listener、 filter、servlet 加载顺序及其详解

转&#xff1a; https://www.cnblogs.com/Jeely/p/10762152.html web.xml 中的listener、 filter、servlet 加载顺序及其详解 一、概述 1、启动一个WEB项目的时候&#xff0c;WEB容器会去读取它的配置文件web.xml&#xff0c;读取<listener>和<context-param>两个…

柱状图python_python柱状图一行

编写计算柱状图的python程序有很多种方法。 通过柱状图,我指的是一个计算对象在 iterable 并在字典中输出计数。例如: >>> L abracadabra >>> histogram(L) {a: 5, b: 2, c: 1, d: 1, r: 2} 编写此函数的一种方法是: def histogram(L): d {} for x in L: i…

HashMap 和 HashTable 到底哪不同 ?

转载自 HashMap 和 HashTable 到底哪不同 &#xff1f;代码版本 JDK每一版本都在改进。本文讨论的HashMap和HashTable基于JDK 1.7.0_67。源码见这里 1. 时间 HashTable产生于JDK 1.1&#xff0c;而HashMap产生于JDK 1.2。从时间的维度上来看&#xff0c;HashMap要比HashTable出…

架构师成长之路(内附推荐书籍)

转&#xff1a; https://www.jianshu.com/p/f661f098b88a 想要成为架构师&#xff0c;对技术的深度和广度都有很高的要求&#xff0c;本文列举出成为一个架构师必备的技能和学习路线。 对于学习途径有疑惑或苦恼&#xff0c;或者有优秀资料可以提供的同学&#xff0c;可加留言&…

python 虚拟环境_理解Python虚拟环境

什么是环境既然有所谓的 虚拟环境&#xff08;Virtual Environment&#xff09;&#xff0c;那么首先有必要解释一下&#xff0c;什么是环境。这里的环境&#xff0c;指的就是 Python 代码的运行环境。它应该包含以下信息&#xff1a;Python 解释器&#xff0c;用哪个解释器来执…

java.util.concurrent.locks.Lock文档说明

【1】Lock接口文档描述 1.相比于使用synchronized方法和代码块&#xff0c;锁的出现提供了更广泛的锁操作。 锁允许更灵活的代码结构&#xff0c;具有许多不同的属性&#xff0c;还支持多个关联的Condition条件对象。 2.锁是用于控制多个线程访问共享资源的工具。通常&#…

20 个使用 Java CompletableFuture的例子

转载自 20 个使用 Java CompletableFuture的例子这篇文章介绍 Java 8 的 CompletionStage API和它的标准库的实现 CompletableFuture。API通过例子的方式演示了它的行为&#xff0c;每个例子演示一到两个行为。既然CompletableFuture类实现了CompletionStage接口&#xff0c;首…

fastreport 打印两个list_Smaller And Smarter Python数据结构:合并两个有序链表

原创&#xff1a; 老表 简说Python 今日问题 &#xff1a;翻转链表k个相邻结点"""目标&#xff1a;写一段程序&#xff0c;合并两个有序链表例如&#xff1a;输入-> 1->2->3输入-> 2->5->6->8输出-> 1->2->2->3->5->6-&…

java.util.concurrent.locks.Condition文档说明

【1】Condition接口文档描述 1.Condition类把Object监视器方法&#xff08;wait,nofify, notifyAll&#xff09;分解为不同对象&#xff0c;通过与Lock实现类的合并使用&#xff0c;Condition可以产生每个object都有多个等待集的效果。在Lock实现类替代synchronized方法或语句…

谈谈HashMap线程不安全的体现

转载自 谈谈HashMap线程不安全的体现HashMap的原理以及如何实现&#xff0c;之前在JDK7与JDK8中HashMap的实现中已经说明了。那么&#xff0c;为什么说HashMap是线程不安全的呢&#xff1f;它在多线程环境下&#xff0c;会发生什么情况呢&#xff1f;1. resize死循环我们都知道…

手机打开python文件_使用python在计算机和手机之间通过wifi进行简单的文件传输...

我会使用 paramiko.它安全快速而且非常简单.怎么回事&#xff1f; 所以我们首先导入模块,然后指定日志文件&#xff1a; import paramiko paramiko.util.log_to_file(/tmp/paramiko.log) 我们打开一个SSH传输&#xff1a; host "example.com" port 22 transport p…

10人以下小团队管理手册-学习笔记

【README】 本文总结于《10人以下小团队管理手册》&#xff0c;很nice的一本书&#xff0c;有兴趣的同学可以翻下&#xff1b; 【0】序章 作者作为咨询师发现&#xff0c; 1.下属对主管有不满&#xff0c;主管对下属有怨言&#xff1b; 2.10人以下小团队主管经常会为 如何用…

JavaWeb项目:简易小米商城系统

Web项目&#xff1a;MyShop简易小米商城系统一.系统概述二.系统开发环境三.涉及技术四.系统功能及使用说明五.作者杂谈六.尾声七.gitee地址&#xff1a;&#xff08;源码见文末&#xff09; 一.系统概述 本系统是一个电商系统&#xff0c;可供用户注册&#xff0c;登录&#…

HashMap的实现与优化

转载自 HashMap的实现与优化HashMap的优化与实践 本文是基于作者在github上的Android 问题交流讨论坛提问而产生的一篇文章&#xff0c;也是自己早打算开坑的一篇文章。文章首先介绍了hashMap的一些基本知识&#xff0c;然后介绍了它在JDK8下的实现原理&#xff0c;最后着重介绍…

如何确定python开发环境已经配置好_搭建 python 开发环境 前面安装选位置我直接回车了现在我想测试查看目录该怎么办...

展开全部 1 ubuntu中一般安装e5a48de588b662616964757a686964616f31333431343036后的默认路径如下#!/usr/bin/env python #!/usr/locat/bin/python 如果没有找到&#xff0c;可以通过命令查询python路径whereis python which python 2 Windows一般可以进入python>>> i…

转- java单例模式几种实现方式

转自&#xff1a; https://www.cnblogs.com/ngy0217/p/9006716.html &#xff1b; 单例模式的五种实现方式 1、饿汉式(线程安全&#xff0c;调用效率高&#xff0c;但是不能延时加载)&#xff1a; 1 2 3 4 5 6 7 public class ImageLoader{ private static ImageLoade…