让你实现一个订单超时取消功能,怎么设计?一般会通过定时任务或者消息队列的延迟消息来实现订单超时取消功能。
定时任务定时任务的逻辑就是扫描已创建未支付的订单,判断订单的创建时间与当前时间的差值,如果已经超过了预设的超时时间,比如 10 分钟,那么就将订单状态更新为已取消。
比如可以每 1 分钟扫描一次表,直接根据当前时间和超时时间得到筛选的时间,比如当前是 14:00,那么 13:50 分之前...
让你实现一个分布式单例对象,如何实现?所谓的单例一般指的是一个进程中一个类对应只有一个实例对象,也就是进程唯一。
而分布式,不过是一个机器部署多个服务,还是多个机器部署,本质上就是多进程,所以所谓的分布式单例指的是这个实例对应需要在多进程中保持唯一。
按照这个思路,我们仅需控制同一时刻,只会有一个进程使用这个单例对象即可,而分布式场景下的分布式锁就很容易实现这个功能。
多个进程竞争分布式锁,...
朋友圈点赞功能如何实现,简单说说?首先我们要理清朋友圈点赞具体需要涉及哪几个功能点:
1)存储点赞信息
需要存储哪些用户点赞了这条朋友圈,具体需要存储用户ID、点赞时间即可。
2)取消点赞
需要快速找到这名用户,将其移除点赞列表。
3)获取点赞列表
朋友圈需要展示点赞的用户头像列表信息.
核心就是这么三点,其实就是增删查,那用什么来实现比较合适呢?
实现快速存储和删除,Set 就挺合适,而且...
如果让你统计每个接口每分钟调用次数怎么统计?最简单的可以使用 ConcurrentHashMap + AtomicInteger + 定时任务实现内存中的统计。
ConcurrentHashMap 的 key 为方法的名称、value 为 AtomicInteger 类型,记录调用次数,可以通过 aop 切面实现每个方法调用都记录到 ConcurrentHashMap 中,然后利用定时任务每...
商家想要知道自己店铺卖的最好的 top 50 商品,如何实现这个功能?根据问题推断这个排行榜是一个动态的排行榜,店铺内商品一直在售卖,因此排行榜也会一直在变,所以如果利用数据库来排行计算效率会非常低,总不能每卖一单就利用数据库排序计算得到排行榜吧?
所以排除数据库这个方案。
应对数据库效率差的情况,我们常用的替代方案就是缓存,而 Redis 内有个 zset 可以实现排行榜的功能。
每个商家...
HashMap 是不是线程安全的?如果让你来实现一个线程安全的 HashMap 你要怎么设计?如果不用加锁你要怎么设计?回答重点HashMap 是 非线程安全 的。因为 HashMap 的内部实现并没有加锁,多个线程同时访问和修改时可能会引发数据竞争,导致数据不一致或陷入死循环等问题。
如何实现一个线程安全的 HashMap要实现一个线程安全的 HashMap,有多种设计方案,下面是几种常见...
什么是配置中心?有哪些常见的配置中心?回答重点配置中心是一个用于配置集中化管理且支持动态更新、分发配置文件的工具(服务)。
它实现了配置的统一管理和动态刷新。当配置信息发生变化时,配置中心可以自动通知服务实例进行配置更新,这样就可以实例无需重启即可应用最新的配置,从一定程度上减少了系统访问的空窗期,非常灵活方便。
常见的配置中心:
Spring Cloud Config:Spring 提...
为什么 Netty 不使用 ThreadLocal 而是自定义了一个 FastThreadLocal ?我们先来思考下 ThreadLocal 有哪些不够好的地方,如果要改都需要改哪些实现。
ThreadLocal hash 冲突的线性探测法不好,还有 Entry 的弱引用可能会发生内存泄漏,这些都和 ThreadLocalMap 有关,所以需要搞个新的 map 来替换 ThreadLoca...
你了解 Java 的 ZGC(Z Garbage Collector)吗?回答重点ZGC(Z Garbage Collector)是Java 11引入的低延迟垃圾回收器,旨在支持大内存(可以高达数 TB)的应用程序,并保持短的GC停顿时间。
主要特点:
并发收集:ZGC的收集过程大部分是在应用线程运行的同时进行,最大限度地减少了停顿时间。
指针压缩:使用指针压缩技术,减少内存占用并提高性能...
什么情况下需要使用分布式事务,有哪些方案?回答重点一般在跨多个数据库、或者不同服务的情况下需要用到分布式事务,比如订单服务和库存服务,下订单和扣库存属于不同服务的方法,因此本地事务无法保证一致性,需要引入分布式服务。
分布式事务是由多个本地事务组成的,分布式事务跨越了多设备,之间又经历的复杂的网络,可想而知想要实现严格的事务道路阻且长。
常见的分布式事务方案:
2PC:基于两阶段提交(2P...
Seata 支持哪些模式的分布式事务?回答重点Seata 目前支持四种事务模式,分别是 AT、TCC、Saga 以及 XA 模式:
AT 模式:通过代理数据库操作来实现分布式事务管理。Seata 在业务操作前后自动生成回滚日志,在提交时直接提交本地事务,在回滚时利用日志进行数据的还原。
TCC 模式:是一种两阶段提交。将一个操作拆分为三个步骤:Try(预留资源)、Confirm(确认操作)...
什么是限流?限流算法有哪些?怎么实现的?限流是什么?首先来解释下什么是限流?
在日常生活中限流很常见,例如去有些景区玩,每天售卖的门票数是有限的,例如 2000 张,即每天最多只有 2000 个人能进去游玩。
那在我们工程上限流是什么呢?限制的是 「流」,在不同场景下「流」的定义不同,可以是每秒请求数、每秒事务处理数、网络流量等等。
而通常我们说的限流指代的是 限制到达系统的并发请求数,使得...
Go 语言中如何访问私有成员?重点回答在 Go 语言中,以小写字母开头的标识符是私有成员,私有成员(字段、方法、函数等)遵循语言的可见性规则,仅在定义它的包内可见,包外无法访问这些私有成员。如果想要访问私有成员,主要包括以下三种方式:
在同一个包内,可以直接访问小写字母开头的私有成员。
在其他包中,无法直接访问私有成员,但可以通过公开的接口来间接访问私有成员。
使用反射来绕过 Go 语言的...
Go 语言使用断言时会发生拷贝吗?回答重点在 Go 语言中,类型断言是否发生拷贝取决于接口内部持有的数据类型:
值类型:当接口持有的是值类型(例如 int、float、struct 等),进行类型断言时会发生拷贝,因为接口存储的是这个值的副本,断言后得到的是该值的拷贝。
引用类型:当接口持有的是引用类型(例如指针、切片、映射、通道等),进行类型断言时不会发生拷贝,因为接口存储的是一个引用,...
Go 语言的接口是怎么实现的?回答重点在 Go 语言中,接口(interface)是一种动态类型,允许定义对象的行为,而不需要指定具体的实现。
它本质上是一个动态类型和动态值的组合:
动态类型:接口持有的具体数据的类型。
动态值:接口持有的具体数据的值或引用。
接口通过这两部分,实现对不同类型的统一操作。
Go 采用鸭子类型的设计哲学,不需要显式声明实现关系。只要一个类型的方法集满足接口...
Go 语言中怎么实现闭包?闭包的主要应用场景是什么?回答重点在 Go 语言中,闭包(Closure)是一个函数值,它可以引用其外部作用域中的变量。在 Go 中实现闭包的方法非常简单,我们可以通过在一个函数内部定义另一个函数,并让其访问外部函数的变量来实现。
即函数可以访问被引用的变量并对其赋值,函数被“绑定”到变量上。
下面是一个简单的 Go 语言闭包示例:
12345678910111...
Go 语言中触发异常的场景有哪些?重点回答在 Go 语言中,使用 error 类型来处理错误,并通过 panic 和 recover 来处理程序的异常情况。以下是一些可能触发 panic(即异常)的场景:
数组或切片越界
空指针解引用
调用 panic 函数
非法类型断言
数学错误
内存越界或非法操作
运行时错误
使用不安全的库或代码
在上述1、2、4和5是在写代码中最常遇见的异常场景。...
Go 语言中 defer 的变量快照在什么情况下会失效?重点回答在 Go 语言中,defer 的变量快照是指在 defer 语句定义时所捕获的变量的状态。但有些情况下,defer 语句中的变量快照可能会失效,导致不如预期那样行为,如下:
1)匿名函数闭包:当 defer 语句中使用的匿名函数捕获了外部变量时。如果变量的值在 defer 语句定义后发生变化,defer 执行时会使用变化后的值。...
Go 语言中通过指针变量 p 访问其成员变量 title,有哪几种方式?回答重点在 Go 语言中,通过指针变量 p 访问其成员变量 title 主要有以下两种方式:
1)使用 (*p).title 访问成员变量。
2)由于 Go 提供了指针的简写支持,还可以直接使用 p.title 来访问成员变量。
这两种方式其实是等价的,Go 编译器会帮你处理其中的细节。
有 4 种情况可以使用简洁支持:...
不分配内存的指针类型能在 Go 语言中使用吗?回答重点在 Go 语言中,不分配内存的指针类型可以使用,但是只能用该指针本身,不可以用*去解引用出具体的值,会导致 panic 。这是因为 Go 允许声明指针变量,但如果不分配内存(没有指向有效的地址),该指针会是 nil。访问 nil 指针会导致运行时错误。
简单的说,Go 中声明一个指针变量是非常直接的,你可以使用 *Type 来声明一个指针...
Go 语言中的局部变量是分配在栈上还是堆上?回答重点Go 语言中的局部变量既可能分配在栈上,也可能分配在堆上
如果变量的生命周期局限于函数作用域,并且不会逃逸到函数外,则分配在栈上。
如果局部变量的生命周期超出函数作用域(如通过指针返回给外部使用),编译器会将变量分配在堆上,确保变量在作用域外仍然有效,这种机制称为“逃逸分析”。
扩展知识栈和堆的区别栈分配:
栈是线程私有的,分配和释放内存...
Go 语言中所有的 T 类型都有 *T 类型吗?重点回答不是。在 Go 语言中,几乎所有的类型 T 都可以有一个对应的指针类型 *T,不过接口类型的指针是无效的。
扩展知识1)普通情况对于大多数类型(包括基础类型、自定义类型、结构体、切片、映射、通道等),你可以使用 *T 来表示类型 T 的指针。以下是一些示例:
基础类型:
12var a int = 10var p *int = &...
Go 语言切片的容量是如何增长的?回答重点在 Go 语言中,切片的容量是一种动态增长的机制。当切片的长度达到或超过容量时,Go 语言会自动扩展其底层数组的容量,一般由append触发。切片容量增长(growslice)的具体规则在不同版本的规则不同。
对于 go1.18 之前来说:
如果期望容量大于当前容量的两倍就会使用期望容量;
如果当前切片的长度小于 1024 的话, growslic...
GO语言中非接口的任意类型T都能调用*T的方法么?反过来呢?回答重点都可以的。在Go语言中,对于非接口的任意类型T,确实可以调用 * T(指向T的指针)的方法。这是因为当你尝试在一个T类型的值上调用一个 * T 方法时,Go编译器会隐式地获取该值的地址,然后调用相应的方法。这种行为被称为指针接收者的方法调用的自动解引用。
示例代码1234567891011121314151617181920...
Go 语言中 init() 函数在什么时候执行?回答重点init() 函数在 Go 程序执行之前自动调用,会在 main() 函数执行之前。
它用于初始化包级别的变量,用来设置初始状态或者执行一次性初始化操作(它不能有参数,也不能返回值)。每个包中的 init() 函数在该包的其他代码执行之前运行,每个包可以有多个 init() 函数。
执行顺序:
包的初始化顺序:如果一个包被多个包依赖,...
Java 中的序列化和反序列化是什么?回答重点序列化是将对象转换为字节流的过程,这样对象可以通过网络传输、持久化存储或者缓存。Java 提供了 java.io.Serializable 接口来支持序列化,只要类实现了这个接口,就可以将该类的对象进行序列化。
反序列化是将字节流重新转换为对象的过程,即从存储中读取数据并重新创建对象。
其它
应用场景:包括网络传输、远程调用、持久化存储(如保存到...
线程的生命周期在 Java 中是如何定义的?回答重点在 Java 中,线程的生命周期可以细化为以下几个状态:
New(初始状态):线程对象创建后,但未调用 start() 方法。
Runnable(可运行状态):调用 start() 方法后,线程进入就绪状态,等待 CPU 调度。
Blocked(阻塞状态):线程试图获取一个对象锁而被阻塞。
Waiting(等待状态):线程进入等待状态,需...
你了解 Java 线程池的原理吗?回答重点线程池是一种池化技术,用于预先创建并管理一组线程,避免频繁创建和销毁线程的开销,提高性能和响应速度。
它几个关键的配置包括:核心线程数、最大线程数、空闲存活时间、工作队列、拒绝策略。
主要工作原理如下:
默认情况下线程不会预创建,任务提交之后才会创建线程(不过设置 prestartAllCoreThreads 可以预创建核心线程)。
当核心...
Java 线程池有哪些拒绝策略?回答重点一共提供了 4 种:
1)AbortPolicy,当任务队列满且没有线程空闲,此时添加任务会直接抛出 RejectedExecutionException 错误,这也是默认的拒绝策略。适用于必须通知调用者任务未能被执行的场景。
2)CallerRunsPolicy,当任务队列满且没有线程空闲,此时添加任务由即调用者线程执行。适用于希望通过减缓任务提交速...
你使用过哪些 Java 并发工具类?回答重点比如:ConcurrentHashMap、AtomicInteger、Semaphore、CyclicBarrier、CountDownLatch、BlockingQueue 等等。
这个问题只要把你知道的一些并发类名字说出来就行了,然后等面试官选择其中一个去询问即可(一般需要结合简历中项目的业务场景,所以需要根据自己的业务提前准备)。
具体的并发...