美团后端暑期一面,本来收到感谢信,但又复活了!

news/2024/12/26 12:38:21/文章来源:https://www.cnblogs.com/javaguide/p/18632488

今天来分享的是一位读者的美团暑期实习一面面经,主要是一些常规八股,难度还是有的,部分题目确实不太好回答。

这位同学回答的不是很好,本来是收到感谢信了。结果,过几天又收到复活赛邀请,复活赛倒是打赢了,已oc。

1、线程池的参数

    /*** 用给定的初始参数创建一个新的ThreadPoolExecutor。*/public ThreadPoolExecutor(int corePoolSize,//线程池的核心线程数量int maximumPoolSize,//线程池的最大线程数long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间TimeUnit unit,//时间单位BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可RejectedExecutionHandler handler//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;}

ThreadPoolExecutor 3 个最重要的参数:

  • corePoolSize : 任务队列未达到队列容量时,最大可以同时运行的线程数量。
  • maximumPoolSize : 任务队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。
  • workQueue: 新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。

ThreadPoolExecutor其他常见参数 :

  • keepAliveTime:线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁。
  • unit : keepAliveTime 参数的时间单位。
  • threadFactory :executor 创建新线程的时候会用到。
  • handler :饱和策略(后面会单独详细介绍一下)。

下面这张图可以加深你对线程池中各个参数的相互关系的理解(图片来源:《Java 性能调优实战》):

线程池各个参数的关系

当任务队列未满时,最多可以同时运行的线程数量就是核心线程数。任务队列中存放的任务满了之后,最多可以同时运行的线程数量就是最大线程数。

2、线程池的执行过程

图解线程池实现原理

  1. 如果当前运行的线程数小于核心线程数,那么就会新建一个线程来执行任务。
  2. 如果当前运行的线程数等于或大于核心线程数,但是小于最大线程数,那么就把该任务放入到任务队列里等待执行。
  3. 如果向任务队列投放任务失败(任务队列已经满了),但是当前运行的线程数是小于最大线程数的,就新建一个线程来执行任务。
  4. 如果当前运行的线程数已经等同于最大线程数了,新建线程将会使当前运行的线程超出最大线程数,那么当前任务会被拒绝,饱和策略会调用RejectedExecutionHandler.rejectedExecution()方法。

3、Excetors工具类提供了哪些线程池?有什么问题吗?

Excetors 工具类提供的常见线程池如下:

  • FixedThreadPool:固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
  • SingleThreadExecutor: 只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
  • CachedThreadPool: 可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
  • ScheduledThreadPool:给定的延迟后运行任务或者定期执行任务的线程池。

Excetors 工具类提供的常见线程池的弊端如下:

  • FixedThreadPoolSingleThreadExecutor:使用的是无界的 LinkedBlockingQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。
  • CachedThreadPool:使用的是同步队列 SynchronousQueue, 允许创建的线程数量为 Integer.MAX_VALUE ,如果任务数量过多且执行速度较慢,可能会创建大量的线程,从而导致 OOM。
  • ScheduledThreadPoolSingleThreadScheduledExecutor:使用的无界的延迟阻塞队列DelayedWorkQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。

更多 Java 并发相关的面试题,可以参考这几篇文章:

  • Java 并发常见面试题总结(上)
  • Java 并发常见面试题总结(中)
  • Java 并发常见面试题总结(下)

4、缓存和数据库一致性如何保证?

细说的话可以扯很多,但是我觉得其实没太大必要(小声 BB:很多解决方案我也没太弄明白)。我个人觉得引入缓存之后,如果为了短时间的不一致性问题,选择让系统设计变得更加复杂的话,完全没必要。

下面单独对 Cache Aside Pattern(旁路缓存模式) 来聊聊。

Cache Aside Pattern 中遇到写请求是这样的:更新数据库,然后直接删除缓存 。

如果更新数据库成功,而删除缓存这一步失败的情况的话,简单说有两个解决方案:

  1. 缓存失效时间变短(不推荐,治标不治本):我们让缓存数据的过期时间变短,这样的话缓存就会从数据库中加载数据。另外,这种解决办法对于先操作缓存后操作数据库的场景不适用。
  2. 增加缓存更新重试机制(常用):如果缓存服务当前不可用导致缓存删除失败的话,我们就隔一段时间进行重试,重试次数可以自己定。不过,这里更适合引入消息队列实现异步重试,将删除缓存重试的消息投递到消息队列,然后由专门的消费者来重试删除缓存,直到成功。虽然说多引入了一个消息队列,但其整体带来的收益还是要更高一些。

相关文章推荐:缓存和数据库一致性问题,看这篇就够了 - 水滴与银弹。

5、Redis 分布式锁

具体可以参考我写的这篇两篇文章,写的比较详细:

  • 分布式锁介绍
  • 分布式锁常见实现方案总结

6、Redis 持久化机制

具体可以参考我写的这篇文章:Redis 持久化机制详解,写的比较详细。

7、 依赖注入的原理

IoC(Inversion of Control,控制反转) 是 Spring 中一个非常非常重要的概念,它不是什么技术,而是一种解耦的设计思想。IoC 的主要目的是借助于“第三方”(Spring 中的 IoC 容器) 实现具有依赖关系的对象之间的解耦(IOC 容器管理对象,你只管使用即可),从而降低代码之间的耦合度。

IoC 是一个原则,而不是一个模式,以下模式(但不限于)实现了 IoC 原则。

ioc-patterns

Spring IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。 IoC 容器负责创建对象,将对象连接在一起,配置这些对象,并从创建中处理这些对象的整个生命周期,直到它们被完全销毁。

在实际项目中一个 Service 类如果有几百甚至上千个类作为它的底层,我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IOC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。

关于 Spring IOC 的理解,推荐看这一下知乎的一个回答:https://www.zhihu.com/question/23277575/answer/169698662 ,非常不错。

控制反转怎么理解呢? 举个例子:"对象 a 依赖了对象 b,当对象 a 需要使用 对象 b 的时候必须自己去创建。但是当系统引入了 IOC 容器后, 对象 a 和对象 b 之间就失去了直接的联系。这个时候,当对象 a 需要使用 对象 b 的时候, 我们可以指定 IOC 容器去创建一个对象 b 注入到对象 a 中"。 对象 a 获得依赖对象 b 的过程,由主动行为变为了被动行为,控制权反转,这就是控制反转名字的由来。

DI(Dependency Inject,依赖注入)是实现控制反转的一种设计模式,依赖注入就是将实例变量传入到一个对象中去。

8、Spring Bean 的生命周期

个人建议面试少问这种问题!

  1. 创建 Bean 的实例:Bean 容器首先会找到配置文件中的 Bean 定义,然后使用 Java 反射 API 来创建 Bean 的实例。
  2. Bean 属性赋值/填充:为 Bean 设置相关属性和依赖,例如@Autowired 等注解注入的对象、@Value 注入的值、setter方法或构造函数注入依赖和值、@Resource注入的各种资源。
  3. Bean 初始化
    • 如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入 Bean 的名字。
    • 如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。
    • 如果 Bean 实现了 BeanFactoryAware 接口,调用 setBeanFactory()方法,传入 BeanFactory对象的实例。
    • 与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法。
    • 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessBeforeInitialization() 方法
    • 如果 Bean 实现了InitializingBean接口,执行afterPropertiesSet()方法。
    • 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
    • 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessAfterInitialization() 方法。
  4. 销毁 Bean:销毁并不是说要立马把 Bean 给销毁掉,而是把 Bean 的销毁方法先记录下来,将来需要销毁 Bean 或者销毁容器的时候,就调用这些方法去释放 Bean 所持有的资源。
    • 如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
    • 如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的 Bean 销毁方法。或者,也可以直接通过@PreDestroy 注解标记 Bean 销毁之前执行的方法。

AbstractAutowireCapableBeanFactorydoCreateBean() 方法中能看到依次执行了这 4 个阶段:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {// 1. 创建 Bean 的实例BeanWrapper instanceWrapper = null;if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}Object exposedObject = bean;try {// 2. Bean 属性赋值/填充populateBean(beanName, mbd, instanceWrapper);// 3. Bean 初始化exposedObject = initializeBean(beanName, exposedObject, mbd);}// 4. 销毁 Bean-注册回调接口try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}return exposedObject;
}

Aware 接口能让 Bean 能拿到 Spring 容器资源。

Spring 中提供的 Aware 接口主要有:

  1. BeanNameAware:注入当前 bean 对应 beanName;
  2. BeanClassLoaderAware:注入加载当前 bean 的 ClassLoader;
  3. BeanFactoryAware:注入当前 BeanFactory 容器的引用。

BeanPostProcessor 接口是 Spring 为修改 Bean 提供的强大扩展点。

public interface BeanPostProcessor {// 初始化前置处理default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}// 初始化后置处理default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}}
  • postProcessBeforeInitialization:Bean 实例化、属性注入完成后,InitializingBean#afterPropertiesSet方法以及自定义的 init-method 方法之前执行;
  • postProcessAfterInitialization:类似于上面,不过是在 InitializingBean#afterPropertiesSet方法以及自定义的 init-method 方法之后执行。

InitializingBeaninit-method 是 Spring 为 Bean 初始化提供的扩展点。

public interface InitializingBean {// 初始化逻辑void afterPropertiesSet() throws Exception;
}

指定 init-method 方法,指定初始化方法:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="demo" class="com.chaycao.Demo" init-method="init()"/></beans>

如何记忆呢?

  1. 整体上可以简单分为四步:实例化 —> 属性赋值 —> 初始化 —> 销毁。
  2. 初始化这一步涉及到的步骤比较多,包含 Aware 接口的依赖注入、BeanPostProcessor 在初始化前后的处理以及 InitializingBeaninit-method 的初始化操作。
  3. 销毁这一步会注册相关销毁回调接口,最后通过DisposableBeandestory-method 进行销毁。

最后,再分享一张清晰的图解(图源:如何记忆 Spring Bean 的生命周期)。

9、Spring 循环依赖了解吗,怎么解决?

强烈建议面试少问这个问题,又难理解,又没啥用!有啥意义呢?

循环依赖是指 Bean 对象循环引用,是两个或多个 Bean 之间相互持有对方的引用,例如 CircularDependencyA → CircularDependencyB → CircularDependencyA。

@Component
public class CircularDependencyA {@Autowiredprivate CircularDependencyB circB;
}@Component
public class CircularDependencyB {@Autowiredprivate CircularDependencyA circA;
}

单个对象的自我依赖也会出现循环依赖,但这种概率极低,属于是代码编写错误。

@Component
public class CircularDependencyA {@Autowiredprivate CircularDependencyA circA;
}

Spring 框架通过使用三级缓存来解决这个问题,确保即使在循环依赖的情况下也能正确创建 Bean。

// 一级缓存
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);// 三级缓存
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);// 二级缓存
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

简单来说,Spring的三级缓存包括:

  1. 一级缓存(singletonObjects):存放最终形态的 Bean(已经实例化、属性填充、初始化),单例池,为“Spring 的单例属性”⽽⽣。一般情况我们获取 Bean 都是从这里获取的,但是并不是所有的Bean都在单例池里面,例如原型 Bean 就不在里面。
  2. 二级缓存(earlySingletonObjects):存放过渡 Bean(半成品,尚未属性填充),也就是三级缓存中ObjectFactory产生的对象,与三级缓存配合使用的,可以防止 AOP 的情况下,每次调用ObjectFactory#getObject()都是会产生新的代理对象的。
  3. 三级缓存(singletonFactories):存放ObjectFactoryObjectFactorygetObject()方法(最终调用的是getEarlyBeanReference()方法)可以生成原始 Bean 对象或者代理对象(如果 Bean 被AOP切面代理)。三级缓存只会对单例 Bean 生效。

只用两级缓存够吗? 在没有AOP的情况下,确实可以只使用一级和三级缓存来解决循环依赖问题。但是,当涉及到AOP时,二级缓存就显得非常重要了,因为它确保了即使在Bean的创建过程中有多次对早期引用的请求,也始终只返回同一个代理对象,从而避免了创建多个代理对象的问题。

这种机制也有一些缺点,比如增加了内存开销(需要维护三级缓存,也就是三个 Map),降低了性能(需要进行多次检查和转换)。并且,还有少部分情况是不支持循环依赖的,比如非单例的 bean 和@Async注解的 bean 无法支持循环依赖。

SpringBoot 2.6.x 以前是默认允许循环依赖的,也就是说你的代码出现了循环依赖问题,一般情况下也不会报错。SpringBoot 2.6.x 以后官方不再推荐编写存在循环依赖的代码,建议开发者自己写代码的时候去减少不必要的互相依赖。这其实也是我们最应该去做的,循环依赖本身就是一种设计缺陷,我们不应该过度依赖 Spring 而忽视了编码的规范和质量,说不定未来某个 SpringBoot 版本就彻底禁止循环依赖的代码了。

SpringBoot 2.6.x 以后,如果你不想重构循环依赖的代码的话,也可以采用下面这些方法:

  • 在全局配置文件中设置允许循环依赖存在:spring.main.allow-circular-references=true。最简单粗暴的方式,不太推荐。
  • 在导致循环依赖的 Bean 上添加 @Lazy 注解,这是一种比较推荐的方式。@Lazy 用来标识类是否需要延迟加载,可以作用在类上、方法上、构造器上、方法参数上、成员变量中。
  • ……

更多 Java 框架相关的面试题,可以参考这几篇文章:

  • Spring 常见面试题总结
  • SpringBoot 常见面试题总结
  • MyBatis 常见面试题总结
  • Netty 常见面试题总结

10、数据库隔离级别以及每个级别解决的问题

SQL 标准定义了四个隔离级别:

  • READ-UNCOMMITTED(读取未提交) :最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
  • READ-COMMITTED(读取已提交) :允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
  • REPEATABLE-READ(可重复读) :对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • SERIALIZABLE(可串行化) :最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

隔离级别 脏读 不可重复读 幻读
READ-UNCOMMITTED
READ-COMMITTED ×
REPEATABLE-READ × ×
SERIALIZABLE × × ×

MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。我们可以通过SELECT @@tx_isolation;命令来查看,MySQL 8.0 该命令改为SELECT @@transaction_isolation;

MySQL> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+

从上面对 SQL 标准定义了四个隔离级别的介绍可以看出,标准的 SQL 隔离级别定义里,REPEATABLE-READ(可重复读)是不可以防止幻读的。

但是!InnoDB 实现的 REPEATABLE-READ 隔离级别其实是可以解决幻读问题发生的,主要有下面两种情况:

  • 快照读:由 MVCC 机制来保证不出现幻读。
  • 当前读:使用 Next-Key Lock 进行加锁来保证不出现幻读,Next-Key Lock 是行锁(Record Lock)和间隙锁(Gap Lock)的结合,行锁只能锁住已经存在的行,为了避免插入新行,需要依赖间隙锁。

因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 READ-COMMITTED ,但是你要知道的是 InnoDB 存储引擎默认使用 REPEATABLE-READ 并不会有任何性能损失。

InnoDB 存储引擎在分布式事务的情况下一般会用到 SERIALIZABLE 隔离级别。

《MySQL 技术内幕:InnoDB 存储引擎(第 2 版)》7.7 章这样写到:

InnoDB 存储引擎提供了对 XA 事务的支持,并通过 XA 事务来支持分布式事务的实现。分布式事务指的是允许多个独立的事务资源(transactional resources)参与到一个全局的事务中。事务资源通常是关系型数据库系统,但也可以是其他类型的资源。全局事务要求在其中的所有参与的事务要么都提交,要么都回滚,这对于事务原有的 ACID 要求又有了提高。另外,在使用分布式事务时,InnoDB 存储引擎的事务隔离级别必须设置为 SERIALIZABLE。

11、索引叶子节点存储的是什么?

索引的叶子节点存储的内容取决于它是主键索引还是非主键索引。在MySQL的InnoDB存储引擎中,使用B+树作为索引结构,其中:

  • 主键索引的叶子节点存储的是整行数据。
  • 非主键索引(也称为二级索引)的叶子节点存储的是主键的值。

当执行基于非主键的索引查询时,数据库会先在非主键索引的B+树中找到对应的主键值,然后再通过主键索引的B+树来获取最终的数据,这个过程称为回表。

相关阅读:

  • MySQL 常见面试题总结
  • MySQL 索引详解
  • MySQL 三大日志(binlog、redo log 和 undo log)详解
  • MySQL 事务隔离级别详解
  • InnoDB 存储引擎对 MVCC 的实现
  • SQL 语句在 MySQL 中的执行过程

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

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

相关文章

Makefile文件中,两个$的变量变量$$Xxx 与一个$的变量 $Xxx的区别

使用来引用 Makefile 中的变量。使用$$来引用 shell 中的变量,以确保在传递给 shell 时保留单个符号。原文地址:Makefile文件中,两个$的变量变量$$Xxx 与一个$的变量 $Xxx的区别Makefile 中的变量引用 在 Makefile 中,$ 符号用于变量替换,但它的使用方式有一些细微的区别:…

【日记】各位圣诞节快乐呀!(566 字)

正文不知道为什么最近总是做噩梦。昨天晚上梦到我一枪射死鱼儿,然后兄长用一瓶 4 块钱 1L 的冰红茶将我敲死,最后全人类死于小行星撞地球。有一颗小行星刚好降落在我家附近的山上,然后散射出了无数激光,把我家切割成一块一块的。也没塌,不知道哪个巫师用了魔法,把周围的房…

LDA主题模型——Python实现(三)

img { display: block; margin-left: auto; margin-right: auto } table { margin-left: auto; margin-right: auto } LDA假设每个文档都是多个主题的混合,每个主题又是多个词语的混合。它通过识别文档中的词语分布来推断出文档的主题结构。LDA的一个简单比喻是冰淇淋店:每个…

工具大全-dirsearch探测Web目录

目录扫描工具dirsearch非常详细使用方法dirsearch介绍dirsearch是一款开源的、基于Python开发的命令行工具,主要用于对Web服务器进行目录和文件的扫描,以发现潜在的安全漏洞。 dirsearch下载地址: https://github.com/maurosoria/dirsearch 运行环境:必须安装python3为什么…

Sealos Devbox 基础教程:使用 Cursor 从零开发一个 One API 替代品

随着技术的成熟和 AI 的崛起,很多原本需要团队协作才能完成的工作现在都可以通过自动化和智能化的方式完成。于是乎,单个开发者的能力得到了极大的提升 - 借助各种工具,一个人就可以完成开发、测试、运维等整条链路上的工作,渡劫飞升成为真正的 “全干工程师”。 之前我们分…

【详解】低功耗低成本的Open开发方案

Open开发应用需求,有没有低功耗、低成本方案? 今天我们一起来聊聊Air780EP模组。 一、Air780EP核心信息描述 运营商支持: Air780EP模组面向国内的全网通模组,支持移动、电信、联通三大运营商。 仅比Air700E系列相对大一些: Air700ECQ/Air700EAQ/Air700EMQ; 与Air780E系列…

01现代计算机视觉入门之:什么是图片

## 系列文章目录 ## 01现代计算机视觉入门之:什么是图片(6700字/25图) 02现代计算机视觉入门之:什么是视频 03现代计算机视觉入门之:什么是图片特征编码 04现代计算机视觉入门之:什么是图片分类 05现代计算机视觉入门之:什么是目标检测 06现代计算机视觉入门之:什么是图…

微信小程序中supabase在线数据库使用指南

在微信小程序中使用supabase在线数据库可以无需后端和服务器完成个人小项目的开发。本文记录我的使用过程,在阅读本文前,建议您先满足以下条件:持有可用于开发的微信小程序 了解kexue上网第一步 注册与简单使用 首先,我们到https://supabase.com/注册一个账户,并创建一个您…

USB上网快速入门:RNDIS网卡,手把手教你!

本文将带你使用RNDIS的方式,搭配Linux主控终端,快速实现USB上网。 RNDIS是指Remote NDIS,基于USB实现RNDIS实际上就是TCP/IP over USB,就是在USB设备上跑TCP/IP,让USB设备看上去像一块网卡,从而使Linux可以通过USB设备连接网络。 一、Linux驱动配置 1. 虚拟串口 通过串口…

2024.12.26 os lab3

2024.12.26 os lab3 原代码 地址:https://github.com/BUPT-OS/easy_lab/tree/lab3 运行未修改的代码,并且注释掉cout时发生错误: malloc(): corrupted top size如果不注释cout,可以正常运行1.不注释 cout 时堆内存的详细分析 1. 程序启动阶段在程序启动时,堆的初始状态为空…

编译型语言的痛!但无文件落地我可以!!!

再不进来看,哭了我就不管你了!!!声明:本文主要用作技术分享,所有内容仅供参考。任何使用或依赖于本文信息所造成的法律后果均与本人无关。请读者自行判断风险,并遵循相关法律法规。 @目录python 等脚本语言示例演示golang 等静态编译型语言内存文件系统memfd_create 使用…

WPF TabControl 去掉鼠标悬浮效果

1.资源<Window.Resources><Style x:Key="TabStyle" TargetType="TabItem"><Setter Property="TextBlock.FontSize" Value="12" /><Setter Property="Template"><Setter.Value><ControlTem…