presto插件机制揭秘:探索无限可能的数据处理舞台

文章目录
- 1. 前言
- 2. Presto插件架构
- 3. Plugin接口
- 3.1 插件协议
- 3.2 插件实现类
- 4. 插件加载过程
- 4.1 PluginManager
- 5. 插件应用
- 6. 总结
关键词:Presto Plugin
1. 前言
本文源码环境:
presto: prestoDb 0.275版本
-
在Presto框架中插件机制设计是一种非常常见和强大的扩展方式。它可以使软件系统更加灵活和可扩展,允许用户根据自己的需求和偏好自定义和扩展系统功能。在 Presto 这样的分布式 SQL 查询引擎中,插件机制发挥着重要的作用,为用户提供了丰富的扩展能力。
-
Presto 是一个基于内存的分布式查询引擎,旨在快速而高效地处理大规模数据。它被广泛应用于数据分析和处理场景,具有优秀的性能和灵活的查询能力。Presto 其插件架构是构建在 Presto 的核心架构之上,为用户提供了一种可扩展的方式来增强和定制 Presto 的功能。通过插件机制,用户可以加载自定义的插件,以增加新的查询功能、支持新的数据源、实现自定义的函数等。
2. Presto插件架构
在 Presto 插件架构中,插件是一个独立的模块,可以包含一个或多个相关功能的集合。每个插件可以有自己的配置、依赖和生命周期管理。插件可以与 Presto 的核心代码进行交互,使用 Presto 提供的API来扩展和定制系统功能。
Presto 插件可以提供以下功能:
- 数据源扩展:插件可以加载不同类型的数据源驱动程序,允许 Presto 查询和访问各种数据源,如关系型数据库、NoSQL 数据库、对象存储等。
- 函数库扩展:插件可以提供自定义的函数和聚合函数,以满足特定的业务需求。用户可以根据自己的需求加载相应的插件,并使用插件中定义的函数来进行数据处理和计算。
- 认证和授权扩展:插件可以提供自定义的认证和授权机制,允许用户根据自己的安全需求对查询进行访问控制和身份验证。
- 优化器和执行器扩展:插件可以实现自定义的查询优化规则和执行计划算法,以提高查询的性能和效率。
Presto 插件架构的核心组件是 PluginManager 类。PluginManager 类负责插件的加载、注册、维护和生命周期管理。它提供了一组方法来加载插件 JAR 文件,解析插件配置,注册插件,并确保插件的正确初始化和销毁。
通过 PluginManager 类,Presto 可以动态加载和管理插件,使用户能够根据自己的需要轻松地扩展和定制 Presto 的功能。插件的加载和管理过程是一个关键的环节,通过对此过程进行深入的源码分析,我们可以更好地理解 Presto 插件架构的工作原理,为开发和利用插件提供指导和技巧。
3. Plugin接口
Presto Plugin接口主要在Presto-spi模块中,presto-spi 是 Presto 的一个核心模块,它提供了一组公共的接口和服务提供者接口(Service Provider Interface,SPI),被其他模块用来定义和扩展 Presto 的行为和功能,Presto通过SPI模块实现了一种松耦合的插件化架构,使得各种组件和功能可以通过实现接口和服务提供者接口来定制和扩展。这样的架构能够方便地支持不同的数据源和扩展需求,同时保持 Presto 的核心逻辑的完整性和可维护性。
3.1 插件协议
com.facebook.presto.spi#Plugin接口定义的功能方法如下:
public interface Plugin
{// 返回插件ConnectorFactory实现 -- 连接外部数据源default Iterable<ConnectorFactory> getConnectorFactories(){return emptyList();}// 返回插件提供的 BlockEncoding 实现,用于压缩和解压 Presto 内部数据结构,提高内存和网络传输效率default Iterable<BlockEncoding> getBlockEncodings(){return emptyList();}// 返回插件提供的类型(Type)实现,用于扩展 Presto 内置类型,支持更多不同种类、不同格式的数据default Iterable<Type> getTypes(){return emptyList();}// 返回插件提供的 ParametricType 实现,用于支持更复杂的泛型类型,比如 MAP<ARRAY<STRING>> 等default Iterable<ParametricType> getParametricTypes(){return emptyList();}// 返回插件提供的自定义函数实现,可以是 SQL 函数,也可以是自定义聚合函数或标量函数,这可以大幅提高 Presto 的灵活性和扩展性default Set<Class<?>> getFunctions(){return emptySet();}// 返回插件提供的 SystemAccessControlFactory 实现,用于自定义 Presto 的系统访问控制策略,比如授权、资源限制等default Iterable<SystemAccessControlFactory> getSystemAccessControlFactories(){return emptyList();}// 返回插件提供的 PasswordAuthenticatorFactory 实现,用于支持自定义 Presto 的密码认证方式default Iterable<PasswordAuthenticatorFactory> getPasswordAuthenticatorFactories(){return emptyList();}// 返回插件提供的 EventListenerFactory 实现,用于自定义一些事件的监听和处理机制,比如在 SQL 执行前/后添加日志功能等default Iterable<EventListenerFactory> getEventListenerFactories(){return emptyList();}// 返回插件提供的 ResourceGroupConfigurationManagerFactory 实现,用于自定义 Presto 的资源管理策略,比如作业分组、优先级等default Iterable<ResourceGroupConfigurationManagerFactory> getResourceGroupConfigurationManagerFactories(){return emptyList();}// 返回插件提供的 SessionPropertyConfigurationManagerFactory 实现,用于自定义 Presto 的会话属性配置,并在 SQL 执行时按照这些属性进行处理default Iterable<SessionPropertyConfigurationManagerFactory> getSessionPropertyConfigurationManagerFactories(){return emptyList();}// 返回插件提供的 FunctionNamespaceManagerFactory 实现,用于实现 Presto 的函数命名空间管理,这样可以支持不同用户、不同组织、不同数据源之间的函数隔离和共享default Iterable<FunctionNamespaceManagerFactory> getFunctionNamespaceManagerFactories(){return emptyList();}// 返回插件提供的 TempStorageFactory 实现,用于将一些中间结果存储到外部临时存储中,从而避免内存消耗过大,甚至导致 OutOfMemoryError;default Iterable<TempStorageFactory> getTempStorageFactories(){return emptyList();}// 返回插件提供的 QueryPrerequisitesFactory 实现,用于自定义 Presto 执行 SQL Query 前的准备工作,比如生成优化计划前的数据准备、元数据加载等;default Iterable<QueryPrerequisitesFactory> getQueryPrerequisitesFactories(){return emptyList();}// 返回插件提供的 NodeTtlFetcherFactory 实现,用于获取 Presto 集群中各个节点的服务生命周期状态,从而支持动态的节点上下线功能;default Iterable<NodeTtlFetcherFactory> getNodeTtlFetcherFactories(){return emptyList();}// 返回插件提供的 ClusterTtlProviderFactory 实现,用于支持 Presto Query 过期的功能,也就是定时清理历史查询的记录;default Iterable<ClusterTtlProviderFactory> getClusterTtlProviderFactories(){return emptyList();}// 返回插件提供的 ExternalPlanStatisticsProvider 实现,用于收集 Presto 执行计划的运行时统计信息,以便分析和优化执行性能。default Iterable<ExternalPlanStatisticsProvider> getExternalPlanStatisticsProviders(){return emptyList();}}
常见的 Presto 插件功能和作用:
- Connector 插件:用于连接不同的数据源,比如 Hadoop HDFS、Amazon S3、Apache Kafka、MySQL 等,使 Presto 可以查询和分析这些不同数据源中的数据。
- Function 插件:提供新的内置函数或用户自定义函数,丰富 Presto 的查询功能。这些函数可以用于数据转换、数学计算、字符串处理、日期处理等。
- Authentication/Authorization 插件:用于提供认证和授权机制,确保只有授权用户可以访问 Presto 引擎并执行查询操作。这可以通过集成现有的身份验证系统(如 Kerberos)或提供自定义的用户认证和授权逻辑来实现。
- SerDe 插件:提供用于序列化和反序列化数据的解析器,使得 Presto 可以读取和分析不同的数据格式,比如 JSON、Avro、Parquet 等。
- Metadata 插件:用于在 Presto 的元数据系统中添加新的数据源、表和列类型,使 Presto 可以理解和管理这些新的数据结构。
- Connector Manager 插件:负责管理和维护连接到 Presto 引擎的各个数据源连接。它可以提供连接池、连接的生命周期管理等功能。
3.2 插件实现类

Presto 插件常见接口及其对应的实现类:
- Connector:该接口定义了与外部数据源连接的功能。一些常见的 Connector 接口的实现类包括:
○ JdbcConnector:用于连接支持 JDBC 协议的关系型数据库,如 MySQL、PostgreSQL、Oracle 等。
○ HiveConnector:用于连接 Hive 数据仓库,支持查询 Hive 表格和视图。
○ KafkaConnector:用于连接 Apache Kafka 流处理平台,支持读取和写入 Kafka 主题。 - Function:该接口定义了自定义 SQL 函数的功能。一些常见的 Function 接口的实现类包括:
○ ScalarFunction:实现标量函数,接收一个或多个输入参数,并返回一个结果。
○ AggregateFunction:实现聚合函数,对一个数据集进行计算,返回一个聚合结果。
○ WindowFunction:实现窗口函数,对数据窗口进行聚合计算,并返回一个结果集。 - Type:该接口定义了自定义数据类型的功能。一些常见的 Type 接口的实现类包括:
○ ArrayType:实现数组类型,表示包含多个元素的数组。
○ MapType:实现映射类型,表示键值对映射的数据结构。
○ RowType:实现行类型,表示一组具有命名字段的数据。 - Split:该接口定义了查询任务(split)的功能。一些常见的 Split 接口的实现类包括:
○ FileSplit:用于处理文件分割任务。
○ TableSplit:用于处理表格分割任务。
○ PartitionSplit:用于处理分区分割任务。
在 presto-main 、presto-hive、presto-jdbc 等模块中,都提供了相应的实现类来实现上述接口,以便连接不同的数据源、定义自定义函数和类型,并处理查询任务。此外,还可以根据需要自定义实现这些接口,以满足特定需求。
常见的JDBCPlugin(关系库插件); HivePlugin 示例:

4. 插件加载过程
Presto插件加载是在Presto启动时进行如下:
com.facebook.presto.server#run
....
// 创建 Bootstrap 对象:通过创建 Bootstrap 对象来启动 Presto,并传入一个模块列表。
// Bootstrap 是 Presto 提供的一个启动类,用于初始化 Presto 的运行环境并加载必要的模块。
Bootstrap app = new Bootstrap(modules.build());try {// 初始化并获取 Injector:使用 app.initialize() 方法初始化 Bootstrap 对象,// 返回一个 Injector 实例。Injector 是 Guice 框架提供的依赖注入容器,// 用于管理 Presto 中的对象依赖关系。Injector injector = app.initialize();// 加载插件:通过获取 PluginManager 实例,调用 loadPlugins() 方法加载插件。//PluginManager 是 Presto 的插件管理器,负责加载、管理和扩展 Presto 的插件。injector.getInstance(PluginManager.class).loadPlugins();ServerConfig serverConfig = injector.getInstance(ServerConfig.class);if (!serverConfig.isResourceManager()) {injector.getInstance(StaticCatalogStore.class).loadCatalogs();}.......
4.1 PluginManager

Presto的插件管理器(PluginManager)负责加载所有的插件。以下是其基本的工作流程:
- Presto在启动时,会通过配置文件(通常是config.properties)来确定插件的目录(plugin.dir)。
- 插件管理器会扫描这个目录以及其子目录,寻找任何有效的插件。
- 对于每个找到的插件,插件管理器会创建一个新的类加载器,然后使用这个类加载器来加载插件的类。
- 插件管理器会调用插件的ConnectorFactory,创建一个新的Connector实例。
- 这个新的Connector实例会被添加到Presto的全局Connector列表中。
这个过程是在Presto启动时自动进行的,所以所有的插件都会在Presto启动后立即可用。
如果你想要添加一个新的插件,你只需要将插件的jar文件和一个名为presto-plugin.properties的配置文件放到插件目录的一个新的子目录中。然后,你可以重新启动Presto,新的插件就会被自动加载。
注意:在加载插件时,Presto不会检查插件的版本或者兼容性。因此,你需要确保你的插件是与你的Presto版本兼容的。
loadPlugins加载方法如下:




其中加载配置路径由 config.properties文件中plugin.bundles或者plugin.dir 进行指定,plugin.bundles 是一个包含插件 JAR 文件路径的逗号分隔列表。每个 JAR 文件通常包含一个或多个 Presto 插件。这些 JAR 文件可以位于本地文件系统中,也可以是远程位置(如 HDFS 或 S3)。
当 Presto 启动时,它会扫描 plugin.bundles 中指定的 JAR 文件,加载其中的插件。
plugin.dir 则是指定一个目录,Presto 会在启动时扫描该目录下的所有 JAR 文件,并尝试加载其中的插件。与 plugin.bundles 不同的是,plugin.dir 只能指定一个目录路径,而不能包含多个逗号分隔的路径。
plugin.bundles 适用于在启动时加载预定义的插件 JAR 文件,而 plugin.dir 则适用于动态加载特定目录下的所有插件 JAR 文件。选择使用哪种方式取决于你的需求和插件管理的方式。

最终installPlugin()方法就是将插件中定义的各种功能注册到Presto中,使得Presto具备这些功能,扩展了Presto的能力和灵活性。通过安装插件,用户可以根据自己的需求来定制和配置Presto,满足不同场景下的数据处理需求。

5. 插件应用
由于篇幅有限这里仅分析函数的扩展案例:
比如我们要在SQL中实现一个集成支持向量机SVM进行分类模型的训练的机器学习函数,只需要直接集成Plugin

按照 如类似UDF等函数的写法实现自己的Presto机器学习函数
LearnClassifierAggregation 该类定义了三个方法:input()、combine() 和 output(),它们分别对应于聚合函数的三个阶段:输入(即接受输入行数据)、合并(将多个聚合器合并为一个)和输出(生成最终结果)

6. 总结
Presto以其插件机制设计,实现了高度的灵活性和可扩展性。通过插件接口,用户可以轻松地扩展和定制数据源、函数库、认证和授权、优化器和执行器等方面的功能。这种灵活性为用户提供了广泛的扩展和定制选项,使得Presto能够更好地适应多样化的应用场景。
作为一个大数据分布式计算框架,Presto拥有强大的功能和优势。它能够无缝地处理不同数据源、执行分布式内存计算,并具备灵活的执行器和监控功能。这些特性使得Presto在大数据领域的分布式计算环境中具有重要的地位和作用。
综上所述,Presto的插件机制为用户提供了灵活性和可扩展性,使其成为一个功能强大且适用广泛的分布式计算框架。在未来的大数据分析和处理中,Presto有望继续发挥重要的作用,并在不断演进的大数据环境中保持领先地位。
相关文章:
presto插件机制揭秘:探索无限可能的数据处理舞台
文章目录 1. 前言2. Presto插件架构3. Plugin接口3.1 插件协议3.2 插件实现类 4. 插件加载过程4.1 PluginManager 5. 插件应用6. 总结 关键词:Presto Plugin 1. 前言 本文源码环境: presto: prestoDb 0.275版本 在Presto框架中插件机制设计是一种非常常见…...
acwing算法基础之数据结构--并查集算法
目录 1 基础知识2 模板3 工程化 1 基础知识 并查集支持O(1)时间复杂度实现: 将两个集合合并。询问两个元素是否在一个集合中。 基本原理:每个集合用一颗树来表示。树根的编号就是整个集合的编号。每个结点存储它的父结点,p[x]表示x的父结点…...
k8s:二进制搭建 Kubernetes v1.20
目录 1 操作系统初始化配置 2 部署 etcd 集群 2.1 准备签发证书环境 2.2 生成Etcd证书 3 部署 docker引擎 4 部署 Master 组件 5 部署 Worker Node 组件 k8s集群master01:192.168.30.105 kube-apiserver kube-controller-manager kube-scheduler etcd k8s集…...
SpringBoot系列-1启动流程
背景 本文作为SpringBoot系列的开篇,介绍SpringBoot的启动流程,包括Spring容器和Tomcat启动过程。SpringBoot作为流行的微服务框架,其是基于约定和自动装配机制对Spring的封装和增强。 由于前面的Spring系列对Spring容器已经进行了较为细致的…...
【记】一次common模块导入无效的bug
首先Maven clean install无用 然后idea清除缓存重启无用 pom.xml文件重载无效 正确解决路径: 1.检查common模块的父工程导入和自身模块的声明是否正确 默认是继承父工程的groupid,可以不用再声明 2.检查子工程是否引入正确的common,org不要…...
1.Netty概述
原生NIO存在的问题(Netty要解决的问题) 虽然JAVA NIO 和 JAVA AIO框架提供了多路复用IO/异步IO的支持,但是并没有提供给上层“信息格式”的良好封装。JAVA NIO 的 API 使用麻烦,需要熟练掌握 ByteBuffer、Channel、Selector等 , 所以用这些API实现一款真正的网络应…...
YOLO目标检测——真实道路车辆检测数据集【含对应voc、coco和yolo三种格式标签】
实际项目应用:自动驾驶技术研发、交通安全监控数据集说明:真实道路车辆检测数据集,真实场景的高质量图片数据,数据场景丰富标签说明:使用lableimg标注软件标注,标注框质量高,含voc(xml)、coco(j…...
【Solidity】Solidity中的基本数据类型和复合数据类型
1. 基本数据类型 1.1 整数类型 Solidity支持有符号整数和无符号整数,可以指定位数和范围。以下是一些整数类型的示例: int:有符号整数,可以是正数或负数。2,-45,2023 uint:无符号整数&#x…...
Flutter Set存储自定义对象时 如何保证唯一
在Flutter中,Set和List是两种不同的集合类型,List中存储的元素可以重复,Set中存储的元素不可重复。 如果你想在Set中存储自定义对象,你需要确保对象的唯一性。 这可以通过在自定义类中实现hashCode方法和equals方法来实现。 has…...
Docker容器中执行throttle.sh显示权限报错:RTNETLINK answers: Operation not permitted
在模拟通信环境时,我执行了一下命令: bash ./throttle.sh wan但是,出现了权限的报错:RTNETLINK answers: Operation not permitted 解决方案说简单也挺简单,只需要两步完成。但是其实又蛮繁琐,因为需要将…...
【Linux】jdk、tomcat、MySQL环境搭建的配置安装,Linux更改后端端口
一、作用 工具的组合为开发者和系统管理员提供了构建和运行Java应用程序以及存储和管理数据的完整环境。 JDK(Java Development Kit):JDK是Java开发工具包,它提供了开发和运行Java应用程序所需的工具和库。通过安装JDK,…...
【WinForm详细教程七】WinForm中的DataGridView控件
文章目录 1.主要属性DataSource行(Row 相关属性)列(Column 相关属性)单元格(Cell 相关属性)逻辑删除AllowUserToAddRowsAllowUserToDeleteRowsAllowUserToOrderColumns其他布局和行为属性 2.控件中的行、列…...
SpringCloudTencent(上)
SpringCloudTencent 1.PolarisMesh介绍2.北极星具备的功能3.北极星包含的组件4.功能特性1.服务管理1.服务注册2.服务发现3.健康检查 2.配置管理 5.代码实战1.环境准备2.服务注册与发现3.远程调用 1.PolarisMesh介绍 1.北极星是腾讯开源的服务治理平台,致力于解决分…...
linux硬盘挂载(linux 修改某个磁盘挂载到新目录)
文章目录 什么是硬盘挂载linux 修改某个磁盘挂载到新目录 什么是硬盘挂载 在Linux操作系统中,挂载硬盘是将硬盘的分区或者整个硬盘与文件系统关联起来,使得我们可以通过文件系统访问硬盘中的数据。 确认硬盘信息 sudo fdisk -l该命令会列出所有已连接…...
hdlbits系列verilog解答(always块case语句)-33
文章目录 一、问题描述二、verilog源码三、仿真结果一、问题描述 Verilog 中的 case 语句几乎等同于 if-elseif-else 序列,该序列将一个表达式与其他表达式列表进行比较。它的语法和功能与 C 中的 switch 语句不同。 always @(*) begin // This is a combinational circuit …...
3D医学三维技术影像PACS系统源码
一、系统概述 3D医学影像PACS系统,它集影像存储服务器、影像诊断工作站及RIS报告系统于一身,主要有图像处理模块、影像数据管理模块、RIS报告模块、光盘存档模块、DICOM通讯模块、胶片打印输出等模块组成, 具有完善的影像数据库管理功能,强大…...
python 之softmx 函数
文章目录 总的介绍小应用 总的介绍 Softmax函数是一个常用的激活函数,通常用于多类别分类问题中。它将一个实数向量转换为概率分布。这个函数的输出是一个概率分布,表示输入样本属于每个可能类别的概率。 给定一个具有 (K) 个不同数值的实数向量 z (z1…...
第3章_基本select语句
文章目录 SQL概述SQL背景知识SQL分类 SQL语言的规则与规范SQL语言的规则SQL大小写规范注释命令规则(暂时了解)数据导入指令 基本的select语句select ...select ... from列的别名去除重复行空值参与运算着重号查询常数 显示表结构讲课代码课后练习 SQL概述…...
GPT3.5+文心一言+chatGLM 计算和代码生成能力简单对比
chatGLM3刚发布(10.27),打算尝试一下其code和计算能力。 共选取三个问题,难度从中等,偏困难,到困难。测试内容是正好手头上在做的事想让LLM来完成(偷懒),之前都是直接使…...
手搓一个ubuntu自动安装python3.9的sh脚本
#!/bin/bash# Step 1: 更新系统软件包 sudo apt update sudo apt upgrade -y sudo apt install -y software-properties-common# Step 2: 安装Python 3.9的依赖项 sudo apt install -y build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libread…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
GAN模式奔溃的探讨论文综述(一)
简介 简介:今天带来一篇关于GAN的,对于模式奔溃的一个探讨的一个问题,帮助大家更好的解决训练中遇到的一个难题。 论文题目:An in-depth review and analysis of mode collapse in GAN 期刊:Machine Learning 链接:...
【深尚想】TPS54618CQRTERQ1汽车级同步降压转换器电源芯片全面解析
1. 元器件定义与技术特点 TPS54618CQRTERQ1 是德州仪器(TI)推出的一款 汽车级同步降压转换器(DC-DC开关稳压器),属于高性能电源管理芯片。核心特性包括: 输入电压范围:2.95V–6V,输…...
【Java多线程从青铜到王者】单例设计模式(八)
wait和sleep的区别 我们的wait也是提供了一个还有超时时间的版本,sleep也是可以指定时间的,也就是说时间一到就会解除阻塞,继续执行 wait和sleep都能被提前唤醒(虽然时间还没有到也可以提前唤醒),wait能被notify提前唤醒…...
二叉树-144.二叉树的前序遍历-力扣(LeetCode)
一、题目解析 对于递归方法的前序遍历十分简单,但对于一位合格的程序猿而言,需要掌握将递归转化为非递归的能力,毕竟递归调用的时候会调用大量的栈帧,存在栈溢出风险。 二、算法原理 递归调用本质是系统建立栈帧,而非…...
