推荐一家小而美的互联网独角兽

沉默王二

共 7160字,需浏览 15分钟

 ·

2024-08-16 14:04

大家好,我是二哥呀。

今天给大家推荐一家小而美的互联网独角兽——收钱吧,之所以用“小”这个字,并不是因为企业规模小,而是相对于京东的 16000 人,收钱吧 25 届只有 100+ HC。

之所以用“美”这个字,是因为据收钱吧的同学 fish09 反馈,在收钱吧的工作压力不大,算是个幸福快乐的地方:

  • 年轻人特别多,80% 都是 95 后,充满活力和激情。随便问一个收钱吧的人对公司的印象,10 个有 9 个说氛围好。
  • 收钱吧属于行业独角兽,是国内领先的数字化门店综合服务商,行业第一。

那就必须得推荐给大家试一试,毕竟好公司不多,最好提前上车然后把车门焊死(🤣),哦不,提前上车感受收钱吧办公大厦的人性化,据说每一层的装修风格都不一样:赛博朋克、盛唐、摩洛哥等。

毕竟一个希望所有人都能一起赚钱的公司能有什么坏心思。” from 牛客的 fish09。

那今天我们就以《Java 面试指南》中收录的《收钱吧面经同学 1 技术一面》为例,来看看收钱吧的面试官都喜欢问哪些问题,好做到心中有数,面试不慌(😄)。

让天下所有的面渣都能逆袭

能看得出来,面试内容仍然是围绕着二哥一直强调的 Java 后端四大件展开,所以准备面试的小伙伴一定要有的放矢,这样才能事半功倍。(面上了记得来给我报喜哦,我想脸上贴贴金 dog)

收钱吧同学1 技术一面面经

一些常见的八股这里就不贴答案了,大家直接去看《面渣逆袭》在线版,我在每道题下面都会标记清楚。

二哥的 Java 进阶之路

系统里面分布式锁是怎么做的?

主要通过 Redisson 框架实现的 RedLock 来完成的。

// 创建 Redisson 客户端配置
Config config = new Config();
config.useClusterServers()
        .addNodeAddress("redis://127.0.0.1:6379",
                "redis://127.0.0.1:6380",
                "redis://127.0.0.1:6381"); // 假设有三个 Redis 节点
// 创建 Redisson 客户端实例
RedissonClient redissonClient = Redisson.create(config);
// 创建 RedLock 对象
RLock redLock = redissonClient.getLock("lock_key");

try {
    // 尝试获取分布式锁,最多尝试 5 秒获取锁,并且锁的有效期为 5000 毫秒
    boolean lockAcquired = redLock.tryLock(55000, TimeUnit.MILLISECONDS);
    if (lockAcquired) {
        // 加锁成功,执行业务代码...
    } else {
        System.out.println("Failed to acquire the lock!");
    }
catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    System.err.println("Interrupted while acquiring the lock");
finally {
    // 无论是否成功获取到锁,在业务逻辑结束后都要释放锁
    if (redLock.isLocked()) {
        redLock.unlock();
    }
    // 关闭 Redisson 客户端连接
    redissonClient.shutdown();
}

你提到了redlock,那它机制是怎么样的?

Redlock 是 Redis 作者提出的一种分布式锁实现方案,用于确保在分布式环境下安全可靠地获取锁。它的目标是在分布式系统中提供一种高可用、高容错的锁机制,确保在同一时刻,只有一个客户端能够成功获得锁,从而实现对共享资源的互斥访问。

Redisson 中的 RedLock 是基于 RedissonMultiLock(联锁)实现的。

二哥的 Java 进阶之路:RedissonRedLock

RedissonMultiLock 的 tryLock 方法会在指定的 Redis 实例上逐一尝试获取锁。

在获取锁的过程中,Redlock 会根据配置的 waitTime(最大等待时间)和 leaseTime(锁的持有时间)进行灵活控制。比如,如果获取锁的时间小于锁的有效期(通过TTL命令获取锁的剩余时间),则表示获取锁成功。

通常,至少需要多数(如 5 个实例中的 3 个)实例成功获取锁,才能认为整个锁获取成功。

如果指定了锁的持有时间(leaseTime),在成功获取锁后,Redlock 会为锁进行续期,以防止锁在操作完成之前意外失效。

红锁能不能保证百分百上锁?

Redlock 不能保证百分百上锁,因为在分布式系统中,网络延迟、时钟漂移、Redis 实例宕机等因素都可能导致锁的获取失败。

Redis解决单点故障主要靠什么?

主从复制,当主节点发生故障时,可以通过手动或自动方式将某个从节点提升为新的主节点,继续对外提供服务,从而避免单点故障。

Redis 的哨兵机制(Sentinel)可以实现自动化的故障转移,当主节点宕机时,哨兵会自动将一个从节点升级为新的主节点。

另外,集群模式下,当某个节点发生故障时,Redis Cluster 会自动将请求路由到其他节点,并通过从节点进行故障恢复。

主从模式用的是异步还是同步?

三分恶面渣逆袭:Redis主从复制简图

在 Redis 主从架构中,主节点负责处理所有的写操作,并将这些操作异步复制到从节点。从节点主要用于读取操作,以分担主节点的压力和提高读性能。

RocketMQ的顺序消息?

RocketMQ 实现顺序消息的关键在于保证消息生产和消费过程中严格的顺序控制,即确保同一业务的消息按顺序发送到同一个队列中,并由同一个消费者线程按顺序消费。

三分恶面渣逆袭:顺序消息

局部顺序消息如何实现?

局部顺序消息保证在某个逻辑分区或业务逻辑下的消息顺序,例如同一个订单或用户的消息按顺序消费,而不同订单或用户之间的顺序不做保证。

三分恶面渣逆袭:部分顺序消息

全局顺序消息如何实现?

全局顺序消息保证消息在整个系统范围内的严格顺序,即消息按照生产的顺序被消费。

可以将所有消息发送到一个单独的队列中,确保所有消息按生产顺序发送和消费。

三分恶面渣逆袭:全局顺序消息

你提到了栈帧,那局部变量表除了栈帧还有什么?

Java 虚拟机栈(Java Virtual Machine Stack),通常指的就是“栈”,它的生命周期与线程相同。

当线程执行一个方法时,会创建一个对应的栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息,然后栈帧会被压入栈中。当方法执行完毕后,栈帧会从栈中移除。

三分恶面渣逆袭:Java虚拟机栈

一个什么都没有的空方法,完全空的参数什么都没有,那局部变量表里有没有变量?

对于静态方法,由于不需要访问实例对象(this),因此在局部变量表中不会有任何变量。

对于非静态方法,即使是一个完全空的方法,局部变量表中也会有一个用于存储 this 引用的变量。this 引用指向当前实例对象,在方法调用时被隐式传入。

比如说有这样一段代码:

public class VarDemo1 {
    public void emptyMethod() {
        // 什么都没有
    }

    public static void staticEmptyMethod() {
        // 什么都没有
    }
}

javap -v VarDemo1 命令查看编译后的字节码:

在非静态方法 emptyMethod 的输出中,你会看到类似这样的内容:

二哥的 Java 进阶之路:javap emptyMethod

这里的 locals=1 表示局部变量表有一个变量,即 this,Slot 0 位置存储了 this 引用。

而在静态方法 staticEmptyMethod 的输出中,你会看到类似这样的内容:

二哥的 Java 进阶之路:javap staticEmptyMethod

这里的 locals=0 表示局部变量表为空,因为静态方法没有 this 引用,也没有其他局部变量。

所有对象都在堆上对不对?

在 Java 中,并不是所有对象都严格在堆上分配内存,虽然堆(Heap)是 Java 对象内存分配的主要区域。

在某些情况下,JVM 的即时编译器(JIT)可能会将对象分配在栈上,这被称为逃逸分析(Escape Analysis)。

也就是活,如果编译器确定一个对象不会在方法外部使用(即对象不会逃逸出方法的作用域),那么该对象可以分配在栈上,而不是堆上。

CMS用了什么垃圾回收算法?

三分恶面渣逆袭:Concurrent Mark Sweep收集器运行示意图

CMS(Concurrent Mark Sweep)主要使用了标记-清除算法进行垃圾收集,分 4 大步:

  • 初始标记(Initial Mark):标记所有从 GC Roots 直接可达的对象,这个阶段需要 STW,但速度很快。
  • 并发标记(Concurrent Mark):从初始标记的对象出发,遍历所有对象,标记所有可达的对象。这个阶段是并发进行的,STW。
  • 重新标记(Remark):完成剩余的标记工作,包括处理并发阶段遗留下来的少量变动,这个阶段通常需要短暂的 STW 停顿。
  • 并发清除(Concurrent Sweep):清除未被标记的对象,回收它们占用的内存空间。

你提到了remark,那它remark具体是怎么执行的?三色标记法?

是的,remark 阶段通常会结合三色标记法来执行,确保在并发标记期间所有存活对象都被正确标记。目的是修正并发标记阶段中可能遗漏的对象引用变化。

在 remark 阶段,垃圾收集器会停止应用线程(STW),以确保在这个阶段不会有引用关系的进一步变化。这种暂停通常很短暂。remark 阶段主要包括以下操作:

  1. 处理写屏障记录的引用变化:在并发标记阶段,应用程序可能会更新对象的引用(比如一个黑色对象新增了对一个白色对象的引用),这些变化通过写屏障记录下来。在 remark 阶段,GC 会处理这些记录,确保所有可达对象都正确地标记为灰色或黑色。
  2. 扫描灰色对象:再次遍历灰色对象,处理它们的所有引用,确保引用的对象正确标记为灰色或黑色。
  3. 清理:确保所有引用关系正确处理后,灰色对象标记为黑色,白色对象保持不变。这一步完成后,所有存活对象都应当是黑色的。

内容来源

  • 星球嘉宾三分恶的面渣逆袭:https://javabetter.cn/sidebar/sanfene/nixi.html
  • 二哥的 Java 进阶之路(GitHub 已有 12000+star):https://javabetter.cn

ending

一个人可以走得很快,但一群人才能走得更远。二哥的编程星球已经有 5900 多名球友加入了,如果你也需要一个良好的学习环境,戳链接 🔗 加入我们吧。这是一个编程学习指南 + Java 项目实战 + LeetCode 刷题的私密圈子,你可以阅读星球专栏、向二哥提问、帮你制定学习计划、和球友一起打卡成长。

两个置顶帖「球友必看」和「知识图谱」里已经沉淀了非常多优质的学习资源,相信能帮助你走的更快、更稳、更远

欢迎点击左下角阅读原文了解二哥的编程星球,这可能是你学习求职路上最有含金量的一次点击。

最后,把二哥的座右铭送给大家:没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟。共勉 💪。

浏览 1519
3点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
3点赞
评论
收藏
分享

手机扫一扫分享

分享
举报