自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(179)
  • 收藏
  • 关注

原创 RocketMQ集群搭建方式

●Broker部署相对复杂,Broker分为Master与Slave,一个Master可以对应多个Slave,但是一个Slave只能对应一个Master,Master与Slave的对应关系通过指定相同的BrokerName,不同的Brokerld来定义,Brokerld为0表示Master,非0表示Slave。●优点:即使磁盘损坏,消息丢失的非常少,且消息实时性不会受影响,同时Master宕机后,消费者仍然可以从Slave消费,而且此过程对应用透明,不需要人工干预,性能同多Master模式几乎一样;

2023-09-04 11:00:00 183

原创 RocketMQ的NameServer

在分布式系统中,即使是对等部署的服务,因为请求到达的时间,硬件的状态,操作系统的调度,虚拟机的GC等,任何一个时间点,这些对等部署的节点状态也不可能完全一致,而流量不一致的情况下,只要注册中心在A承诺的时间内(例如1s内)将数据收敛到一致状态(即满足最终一致),流量将很快趋于统计学意义上的一致,所以注册中心以最终一致的模型设计在生产实践中完全可以接受。在服务发现中,服务调用发起方更关注的是其要调用的服务的实时的地址列表和实时健康状态,每次发起调用时,并不关心要调用的服务的历史服务地址列表、过去的健康状态。

2023-09-03 10:30:00 267

原创 RocketMQ的Broker

如果对消息的可靠性要求比较严格,可以采用SYNC_MASTER加SLAV E的部署方式。如果只是测试方便,则可以选择仅ASYNC_MASTER或仅SYNC_MASTER的部署方式。SYNC_FLUSH (同步刷新)相比于ASYNC_FLUSH (异步处理)会损失很多性能,但是也更可靠,所以需要根据实际的业务场景做好权衡。ASYNC_FLUSH 模式下的 broker 则利 用刷盘一组消息的模式,可以取得更好的性能。SYNC_MASTER表示当前broker是一个同步复制的Master。

2023-09-02 09:45:00 390

原创 RocketMQ消费者

这条消息的消费过程中有4次与DB的交互,如果按照每次5ms计算,那么总共耗时20ms,假设业务计算耗时5ms,那么总过耗时25ms,所以如果能把4次DB交互优化为2次,那么总耗时就可以优化到15ms,即总体性能提高了40%。例如订单扣款类应用,一次处理一个订单耗时1 s,一次处理10个订单可能也只耗时2s,这样即可大幅度提高消费的吞吐量,通过设置consumer的consumeMessageBatchMaxSize返个参数,默认是1,即一次只消费一条消息,例如设置为N,那么每次消费的消息数小于等于N。

2023-09-01 10:25:24 268

原创 RocketMQ生产者

所以,一次消息发送的耗时时间是上述三个步骤的总和,而某些场景要求耗时非常短,但是对可靠性要求并不高,例如日志收集类应用,此类应用可以采用oneway形式调用,oneway形式只发送请求不等待应答,而发送请求在客户端实现层面仅仅是一个操作系统系统调用的开销,即将数据写入客户端的socket缓冲区,此过程耗时通常在微秒级。如果业务对消息可靠性要求比较高,建议应用增加相应的重试逻辑:比如调用send同步方法发送失败时,则尝试将消息存储到db,然后由后台线程定时重试,确保消息一定到达Broker。

2023-08-31 10:00:00 207

原创 RocketMQ消息优先级

TypeA、TypeB、TypeC三类消息。对这种要求,或者逻辑更复杂的要求,就要用户自己编码实现优先级控制,如果上述的三类消息在一个Topic里,可以使用PullConsumer,自主控制MessageQueue的遍历,以及消息的读取;多个不同的消息类型使用同一个topic时,由于某一个种消息流量非常大,导致其他类型的消息无法及时消费,造成不公平,所以把流量大的类型消息在一个单独的Topic,其他类型消息在另外一个Topic,应用程序创建两个Consumer,分别订阅不同的Topic,这样就可以了。

2023-08-30 11:45:00 201

原创 RocketMQ消息查询

区别于消息消费:先尝后买尝就是消息查询买:消息的消费RocketMQ支持按照下面两种维度("按照Message ld查询消息"、"按照Message Key查询消息")进行消息查询。

2023-08-29 11:15:00 247

原创 RocketMQ事务消息

对于Rollback,本身一阶段的消息对用户是不可见的,其实不需要真正撤销消息(实际上RocketMQ也无法去真正的删除一条消息,因为是顺序写文件的)。Broker端对未确定状态的消息发起回查,将消息发送到对应的Producer端(同一个Group的Producer),由Producer根据消息来检查本地事务的状态,进而执行Commit或者Rollback,Broker端通过对比Half消息和Op消息进行事务消息的回查并且推进CheckPoint (记录那些事务消息的状态是确定的)。

2023-08-28 10:30:00 85

原创 RocketMQ同步复制和异步复制

通常情况下,应该把Master和Save配置成ASYNC_FLUSH的刷盘方式,主从之间配置成SYNC_MASTER的复制方式,这样即使有一台机器出故障,仍然能保证数据不丢,是个不错的选择。同步复制和异步复制是通过Broker配置文件里的brokerRole参数进行设置的,这个参数可以被设置成ASYNC_MASTER、SYNC_MASTER、SLAVE三个值中的一个。在同步复制方式下,如果Master出故障,Slave上有全部的备份数据,容易恢复,但是同步复制会增大数据写入延迟,降低系统吞吐量。

2023-08-27 11:00:00 331

原创 RocketMQ零拷贝原理

当进程在访问这段地址时,通过查找页表,发现虚拟内存对应的页没有在物理内存中缓存,则产生"缺页",由内核的缺页异常处理程序处理,将文件对应内容,以页为单位(4096)加载到物理内存,注意是只加载缺页,但也会受操作系统一些调度策略影响,加载的比所需的多。通过buffer可以减少进程间通信需要等待的时间,当存储速度快的设备与存储速度慢的设备进行通信时,存储慢的数据先把数据存放到buffer,达到一定程度存储快的设备再读取buffer的数据,在此期间存储快的设备CPU可以干其他的事情。

2023-08-26 10:30:00 188

原创 RocketMQ消息存储

Apache下开源的另外一款MQ—ActiveMQ (默认采用的KahaDB做消息存储)可选用JDBC的方式来做消息持久化,通过简单的xmI配置信息即可实现JDBC消息存储。RocketMQ消息的存储是由ConsumeQueue和CommitLog配合完成的,消息真正的物理存储文件是CommitLog,ConsumeQueue 是消息的逻辑队列,类似数据库的索引文件,存储的是指向物理存储的地址。(2) ConsumeQueue:消息消费队列,引入的目的主要是提高消息消费的性能。

2023-08-25 11:40:28 122

原创 RocketMQ消息发送及消费

另一种提高发送速度的方法是增加Producer的并发量,使用多个Producer同时发送,我们不用担心多Producer同时写会降低消息写磁盘的效率,RocketMQ引入了一个并发窗口,在窗口内消息可以并发地写入DirectMem中,然后异步地将连续一段无空洞的数据刷入文件系统当中。Consumer在消费的过程中,如果发现由于某种原因发生严重的消息堆积,短时间无法消除堆积,这个时候可以选择丢弃不重要的消息,使Consumer尽快追上Producer的进度。想保证不丢消息,可以设置多重试几次。

2023-08-24 11:15:00 356

原创 RocketMQ架构

Master也可以部署多个。在RocketMQ中,所有消息队列都是持久化的,长度无限的数据结构,所谓长度无限是指队列中的每个存储单元都是定长,访问其中的存储单元使用offset来访问,offset为java long类型,64位,理论上在100年内不会溢出,所以认为为是长度无限,另外队列中只保存最近几天的数据,之前的数据会按照过期时间来删除。顺序消息的一种,正常情况下可以保证完全的顺序消息,但是一旦发生通信异常,Broker重启,由于队列总数发生发化,哈希取模后定位的队列会发化,产生短暂的消息顺序不一致。

2023-08-23 10:45:00 106

原创 ZooKeeper各服务器角色介绍

在事务请求提交阶段,针对Follower服务器,Leader 仅仅只需要发送一个COMMIT消息,Follower 服务器就可以完成事务请求的提交了,因为在这之前的事务请求投票阶段,Follower已经接收过PROPOSAL消息,该消息中包含了事务请求的内容,因此Follower可以从之前的Proposal缓存中再次获取到事务请求。如果是事务请求,那么Follower就会将该事务请求转发给Leader 服务器,Leader服务器在接收到这个事务请求后,就会将其提交到请求处理链,按照正常事务请求进行处理。

2023-08-22 09:45:00 312

原创 ZooKeeper的Leader选举实现细节

那么如此重复发送是否会导致其他问题呢?另外还有一个细节需要注意,就是在完成步骤9之后,如果统计投票发现已经有过半的服务器认可了当前的选票,这个时候,ZooKeeper并不会立即进入步骤10来更新服务器状态,而是会等待一段时间(默认是200毫秒)来确定是否有新的更优的投票。如果服务器发现自己的选举轮次已经落后于该外部投票对应服务器的选举轮次,那么就会立即更新自己的选举轮次(logicalclock),并且清空所有已经收到的投票,然后使用初始化的投票来进行PK以确定是否变更内部投票,最终再将内部投票发送出去。

2023-08-21 09:15:00 58

原创 ZooKeeper的Leader选举概述及算法分析

下文即仅对此算法进行介绍。由于是初始情况,因此对于Server1和Server2来说,都会将自己作为Leader服务器来进行投票,每次投票包含的最基本的元素包括:所推举的服务器的myid和ZXID,我们以(myid,ZXID)的形式来表示。因为是初始化阶段,因此无论是Server1还是Server2,都会投给自己,即Server1的投票为(1,0),Server2的投票为(2,0),然后各自将这个投票发给集群中其他所有机器。于是SID为3、4和5的机器,投票情况分别为:(3,9)、(4,8)和(5,8)。

2023-08-20 10:45:00 117

原创 ZooKeeper集群服务器启动

ZooKeeper的Leader选举过程,简单地讲,就是一个集群中所有的机器相互之间进行一系列投票,选举产生最合适的机器成为Leader,同时其余机器成为Follower或是Observer的集群机器角色初始化过程。当和Leader建立起连接后,Learner就会开始向Leader进行注册一所谓的注册,其实就是将Learner 服务器自己的基本信息发送给Leader 服务器,我们称之为LearnerInfo,包括当前服务器的SID和服务器处理的最新的ZXID。

2023-08-19 10:30:00 435

原创 ZooKeeper单机服务器启动

ZooKeeper的请求处理方式是典型的责任链模式的实现,在ZooKeeper服务器上,会有多个请求处理器依次来处理一个客户端请求。ZooKeeper服务器首先会进行服务器实例的创建,接下去的步骤则都是对该服务器实例的初始化工作,包括连接器、内存数据库和请求处理器等组件的初始化。需要注意的一点是,虽然这里ZooKeeper的NIO服务器已经对外开放端口,客户端能够访问到ZooKeeper的客户端服务端口2181,但是此时ZooKeeper服务器是无法正常处理客户端请求的。关于ZooKeeper参数配置。

2023-08-18 11:45:00 534

原创 ZooKeeper的应用场景(分布式锁、分布式队列)

客户端无端地接收到过多和自己并不相关的事件通知,如果在集群规模比较大的情况下,不仅会对ZooKeeper服务器造成巨大的性能影响和网络冲击,更为严重的是,如果同一时间有多个节点对应的客户端完成事务或是事务中断引起节点消失,ZooKeeper服务器就会在短时间内向其余客户端发送大量的事件通知一这就是所谓的羊群效应。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要通过一些互斥手段来防止彼此之间的干扰,以保证一致性,在这种情况下,就需要使用分布式锁了。

2023-08-17 16:58:56 598 1

原创 ZooKeeper的应用场景(集群管理、Master选举)

但是,同时还需要注意的一点是:在分布式日志收集这个场景中,收集器节点上还会存放所有已经分配给该收集器机器的日志源机器列表,如果只是简单地依靠ZooKeeper自身的临时节点机制,那么当一个收集器机器挂掉或是当这个收集器机器中断“心跳汇报”的时候,待该收集器节点的会话失效后,ZooKeeper就会立即删除该节点,于是,记录在该节点上的所有日志源机器列表也就随之被清除掉了。接下去,我们将重点来看Master选举的过程,首先来明确下Master选举的需求:在集群的所有机器中选举出一台机器作为Master。

2023-08-16 17:15:00 1407

原创 ZooKeeper的应用场景(命名服务、分布式协调通知)

基于ZooKeeper实现分布式协调与通知功能,通常的做法是不同的客户端都对ZooKeeper上同一个数据节点进行Watcher注册,监听数据节点的变化(包括数据节点本身及其子节点),如果数据节点发生变化,那么所有订阅的客户端都能够接收到相应的Watcher通知,并做出相应的处理。在传统的开发中,我们通常是通过主机之间是否可以相互PING通来判断,更复杂一点的话,则会通过在机器之间建立长连接,通过TCP连接固有的心跳检测机制来实现上层机器的心跳检测,这些确实都是一些非常常见的心跳检测方法。

2023-08-16 11:22:05 1383

原创 ZooKeeper的应用场景(数据发布订阅、负载均衡)

ZooKeeper是一个典型的发布/订阅模式的分布式数据管理与协调框架,开发人员可以使用它来进行分布式数据的发布与订阅。另一方面,通过对ZooKeeper中丰富的数据节点类型进行交叉使用,配合Watcher事件通知机制,可以非常方便地构建一系列分布式应用中都会涉及的核心功能,如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等。ZooKeeper是一个高可用的分布式数据管理与协调框架。

2023-08-15 12:04:01 698

原创 ZooKeeper的基本概念

一旦节点被标记上这个属性,那么在这个节点被创建的时候,ZooKeeper会自动在其节点名后面追加上一个整型数字,这个整型数字是一个由父节点维护的自增数字。在前面我们已经提到,ZooKeeper 的每个ZNode上都会存储数据,对应于每个ZNode,ZooKeeper都会为其维护一个叫作Stat的数据结构,Stat中记录了这个ZNode的三个数据版本,分别是version(当前ZNode的版本)、cversion (当前ZNode子节点的版本)和aversion(当前ZNode的ACL版本)。

2023-08-14 22:30:00 1517

原创 ZooKeeper介绍

这里所说的树型结构的名字空间,是指ZooKeeper服务器内存中的一个数据模型,其由一系列被称为ZNode的数据节点组成,总的来说,其数据模型类似于一个文件系统,而ZNode之间的层级关系,就像文件系统的目录结构一样。所有事务请求的处理结果在整个集群中所有机器.上的应用情况是一致的,也就是说,要么整个集群所有机器都成功应用了某一个事务,要么都没有应用,一定不会出现集群中部分机器应用了该事务,而另外一部分没有应用的情况。无论客户端连接的是哪个ZooKeeper服务器,其看到的服务端数据模型都是一致的。

2023-08-14 17:15:08 821

原创 RabbitMQ消息可靠性

你用支付宝给商家支付,如果是个仔细的人,会考虑我转账的话,会不会把我的钱扣了,商家没有收到我的钱?一般我们使用支付宝或微信转账支付的时候,都是扫码,支付,然后立刻得到结果,说你支付了多少钱,如果你绑定的是银行卡,可能这个时候你并没有收到支付的确认消息。往往是在一段时间之后,你会收到银行卡发来的短信,告诉你支付的信息。支付平台如何保证这笔帐不出问题?支付平台必须保证数据正确性,保证数据并发安全性,保证数据最终一致性。支付平台通过如下几种方式保证数据一致性:(1)分布式锁。

2023-08-13 23:55:06 38

原创 SpringBoot整合RabbitMQ

2 application.properties中添加连接信息。5 使用RestController发送消息。4 RabbitConfig类。1 添加starter依赖。6 使用监听器,用于推消息。

2023-08-13 10:15:00 101

原创 RabbitMQ工作流程详解

一旦TCP连接建立起来,客户端紧接着创建一个AMQP 信道(Channel),每个信道都会被指派一个唯一的ID。(1)消费者连接到RabbitMQ Broker,建立一个连接(Connection),开启一个信道(Channel)。(2)消费者向RabbitMQ Broker请求消费相应队列中的消息,可能会设置相应的回调函数,以及做一些准备工作。(1)生产者连接RabbitMQ,建立TCP连接(Connection),开启信道(Channel)(7)如果找到,则将从生产者发送过来的消息存入相应的队列中。

2023-08-12 23:15:00 1067

原创 Redis缓存设计

缓存能够有效地加速应用的读写速度,同时也可以降低后端负载,对日常应用的开发至关重要。但是将缓存加入应用架构后也会带来一些问题,本文将针对这些问题介绍缓存使用技巧和设计方案。

2023-08-12 09:52:40 187

原创 Redis心跳检测

如果因为网络故障,主服务器传播给从服务器的写命令在半路丢失,那么当从服务器向主服务器发送REPLCONFACK命令时,主服务器将发觉从服务器当前的复制偏移量少于自己的复制偏移量,然后主服务器就会根据从服务器提交的复制偏移量,在复制积压缓冲区里面找到从服务器缺少的数据,并将这些数据重新发送给从服务器。注意,主服务器向从服务器补发缺失数据这一操作的原理和部分重同步操作的原理非常相似,这两个操作的区别在于,补发缺失数据操作在主从服务器没有断线的情况下执行,而部分重同步操作则在主从服务器断线并重连之后执行。

2023-08-11 21:45:00 648

原创 Redis复制

在Redis中,用户可以通过执行SLAVEOF命令或者设置slaveof选项,让一个服务器去复制(replicate) 另一个服务器,我们称呼被复制的服务器为主服务器(master),而对主服务器进行复制的服务器则被称为从服务器(slave),如下图所示。

2023-08-11 11:53:17 275

原创 Redis的AOF持久化

从平摊操作的角度来看,no模式和everysec模式的效率类似,当出现故障停机时,使用no模式的服务器将丢失上次同步AOF文件之后的所有写命令数据。下面的表格展示了一个AOF文件重写例子,当子进程开始进行文件重写时,数据库中只有k1一个键,但是当子进程完成AOF文件重写之后,服务器进程的数据库中已经新设置了k2、k3、k4三个键,因此,重写后的AOF文件和服务器当前的数据库状态并不一致,新的AOF文件只保存了k1一个键的数据,而服务器数据库现在却有k1、k2、k3、k4四个键。

2023-08-10 22:45:00 270

原创 Redis的RDB持久化

Redis是一个键值对数据库服务器,服务器中通常包含着任意个非空数据库,而每个非空数据库中又可以包含任意个键值对,为了方便起见,我们将服务器中的非空数据库以及它们的键值对统称为数据库状态。举个例子,下图展示了一个包含三个非空数据库的Redis服务器,这三个数据库以及数据库中的键值对就是该服务器的数据库状态。因为Redis是内存数据库,它将自己的数据库状态储存在内存里面,所以如果不想办法将储存在内存中的数据库状态保存到磁盘里面,那么一旦服务器进程退出,服务器中的数据库状态也会消失不见。

2023-08-10 10:57:41 873

原创 Redis过期键删除策略

另一方面,定时删除策略的缺点是,它对CPU时间是最不友好的:在过期键比较多的情况下,删除过期键这一行为河能会占用相当一部分CPU时间,在内存不紧张但是CPU时间非常紧张的情况下,将CPU时间用在删除和当前任务无关的过期键上,无疑会对服务器的响应时间和吞吐量造成影响。惰性删除策略对CPU时间来说是最友好的:程序只会在取出键时才对键进行过期检查,这可以保证删除过期键的操作只会在非做不可的情况下进行,并且删除的目标仅限于当前处理的键,这个策略不会在删除其他无关的过期键上花费任何CPU时间。

2023-08-09 23:45:00 775

原创 Redis类型检查与命令多态

借用面向对象方面的术语来说,我们可以认为LLEN命令是多态( polymorphism)的,只要执行LLEN命令的是列表键,那么无论值对象使用的是ziplist编码还是linkedlist编码,命令都可以正常执行。从上面发生类型错误的代码示例可以看出,为了确保只有指定类型的键可以执行某些特定的命令,在执行一个类型特定的命令之前,Redis会先检查输入键的类型是否正确,然后再决定是否执行给定的命令。下图展示了LLEN命令从类型检查到根据编码选择实现函数的整个执行过程,其他类型特定命令的执行过程也是类似的。

2023-08-09 11:31:31 375

原创 CompletableFuture基本概念及用法

CompletableFuture继承于java.util.concurrent.Future,它本身具备Future的所有特性,并且基于JDK1.8的流式编程以及Lambda表达式等实现一元操作符、异步回调以及事件驱动编程的异步类,可以用来实现多线程的串行关系,并行关系,聚合关系。它的灵活性和更强大的功能是Future无法比拟的。第三种,thenAccept(如图),第一个任务执行结束后触发第二个任务,并且第一个任务的执行结果作为第二个任务的参数,这个方法是纯粹接受上一个任务的结果,不返回新的计算值。

2023-08-08 11:30:00 93

原创 Java并发系列之八:ThreadPoolExecutor

向阻塞队列offer一个任务,如果阻塞队列已满,那么继续尝试创建worker,不过此时创建的不是核心线程,而是奔着最大线程数去的,如果已经达到了最大线程数,那么触发拒绝策略。在构造函数中,将AQS的state初始化为-1,之所以不初始化为0,是为了在初始化期间不接受中断信号,直到runWorker方法开始运行,即工作线程真的开始处理任务,state将 会被修改为0,此时相当于锁被释放的状态,可以接受中断信号。注释告诉我们当你向线程池提交一个任务,线程池可能会创建一个线程来处理,也可能使用已有的线程处理。

2023-08-07 11:15:00 206

原创 Java并发系列之七:ConcurrentHashMap

首先判断node是否为null,这个判断有点略显奇怪,node只在最开始的三目运算中操作过,若当前线程抢到锁,那么node为null,而这里既然判断node是否为null,是不是也就是说若线程没抢到锁,执行scanAndLockForPut的结果,就是初始化node。因为使用它的风险较高并且场景较少,所以我们在日常的业务代码中几乎看不到对Unsafe的使用,但是对于一些追求高效,并且有能力保证安全的下层组件来说,使用Unsafe是家常便饭。不同HashEntry数组的读写互不干扰,这就形成了所谓的分段锁。

2023-08-06 11:45:00 339

原创 Java并发系列之六:CountDownLatch

当然是子任务都完成的时候,AQS中state这个int值我觉得可以用来表示正在等待执行执行完成的任务数,每当一个任务完成就,state值就自减1,当state为0时,唤醒正在等待的主线程。这样的大致思路,看上去没什么大问题,事实上CountDownLatch确实也是结合AQS这么做的,关键在于细节,细节是魔鬼,因为在并发场景下往往差之毫厘,谬以千里。这里就是CountDownLatch这个同步工具的用武之地,正如它的介绍那样,它能够帮助主线程在等待其他工作线程里的任务完成之后,再继续执行。

2023-08-05 17:15:00 185

原创 Java并发系列之五:ReentranLock

此外,ReentrantLock作为锁实现Lock接口,而Lock定义的方法将会被上层调用,那么接下来就来对方法的实现进行探究,可以大胆猜测,NonfairSync、FairSync已经做了比较完整的封装,ReentrantLock公有方法的实现,可能只是对sync对象的简单调用。在很多情况下,非公平锁的效率更高。2.当state为0,那么代表锁状态为空闲,便可以进行一次CAS来原子地更改state,如果state更改成功,则代表获取了锁,将当前线程置为独占线程,并返回true,否则返回false。

2023-08-04 14:17:58 85

原创 Java并发系列之四:重中之重AQS

上一期我们介绍了乐观锁,而乐观锁的本质即是CAS,操作系统提供了支持CAS修改内存值的原子指令,所以乐观锁得以实现。从软件工程的角度去看,虽然底层已经通过CAS实现了乐观锁,Java的底层已经在Unsafe这个类中封装了compareAndSwap方法,支持了对CAS原语的调用,为了使上层更加易用,需要经过进一步的抽象和封装。抽象这个词虽然简单,但私以为要做出高内聚低耦合的抽象绝对是难点。在Java中最著名的并发包就是JUC,其中的组件和日常Java开发息息相关。

2023-08-03 10:57:22 93

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除