摇人!今年我们新招16000人

共 10567字,需浏览 22分钟

 ·

2024-07-27 14:04

大家好,我是二哥呀。

就在昨天,京东官媒宣布,京东总部 1 号园区 DEF 三座新楼正式启用,并且新招 16000 人,于 8 月 1 日 正式开启 25 届秋招正式批通道!

截图来自京东黑板报

好家伙,这比去年早了 20 多天啊,去年是 8 月 21 日才开启。并且今年的 HC 比去年直接多了 1000,这可不是小数目啊!

京东可能排不到第一梯队的互联网大厂,比如说字节、腾讯、阿里,但排到第二梯队肯定是毫无疑问的,并且京东的薪资待遇真的可以。

截图来自二哥的 Java 面试指南

24 届秋招的时候,我就统计过一波京东的薪资待遇,比如说京东 Java 后端本科 211 就能拿到 23*16 的年包,说实话,一年 37 万左右也是人中龙凤了。

应该有不少小伙伴对京东感兴趣,那接下来我们就通过《Java 面试指南》里收录的《京东面经同学4 云实习》为例,来看看京东面试官都喜欢问哪些问题,好做去新盖的三座大楼体验一下宽敞高效的办公场所(顺带吸一丢丢甲醛😂)。

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

京东同学4云实习面经

之前面经中重复出现的题目这次就略过了,大家可以去三分恶面渣逆袭在线版查看,我已经帮大家标记好了:https://javabetter.cn/sidebar/sanfene/nixi.html

hashmap是会死锁的, 你知道吗

HashMap 不是线程安全的,多线程下扩容会死循环。因为 JDK1.7 中的 HashMap 使用的是头插法插入元素,在多线程的环境下,扩容的时候就有可能导致出现环形链表,造成死循环。

二哥的 Java 进阶之路

不过,JDK 8 时已经修复了这个问题,扩容时会保持链表原来的顺序。

i++是原子操作吗?

原子操作指的是一个操作是不可分割的,要么全部执行成功,要么完全不执行。

i++ 不是一个原子操作,它包括三个步骤:

  1. 从内存中读取 i 的值。
  2. 对 i 进行加 1 操作。
  3. 将新的值写入内存。

假如两个线程同时对 i 进行 i++ 操作时,可能会发生以下情况:

  1. 线程 A 读取 i 的值(假设 i 的初始值为 1)。
  2. 线程 B 也读取 i 的值(值仍然是 1)。
  3. 线程 A 将 i 增加到 2,并将其写回内存。
  4. 线程 B 也将 i 增加到 2,并将其写回内存。

尽管进行了两次递增操作,i 的值只增加了 1 而不是 2。可以使用 synchronized 或 AtomicInteger 确保操作的原子性。

常见的7个GC回收器

就目前来说,JVM 的垃圾收集器主要分为两大类:分代收集器和分区收集器,分代收集器的代表是 CMS,分区收集器的代表是 G1 和 ZGC。

三分恶面渣逆袭:HotSpot虚拟机垃圾收集器

四个引用(强软弱虚)

三分恶面渣逆袭:四种引用总结

强引用是 Java 中最常见的引用类型。使用 new 关键字赋值的引用就是强引用,只要强引用关联着对象,垃圾收集器就不会回收这部分对象。

String str = new String("沉默王二");

软引用是一种相对较弱的引用类型,可以通过 SoftReference 类实现。软引用对象在内存不足时才会被回收。

SoftReference<String> softRef = new SoftReference<>(new String("沉默王二"));

弱引用可以通过 WeakReference 类实现。弱引用对象在下一次垃圾回收时会被回收,不论内存是否充足。

WeakReference<String> weakRef = new WeakReference<>(new String("沉默王二"));

虚引用可以通过 PhantomReference 类实现。虚引用对象在任何时候都可能被回收。主要用于跟踪对象被垃圾回收的状态,可以用于管理直接内存。

PhantomReference<String> phantomRef = new PhantomReference<>(new String("沉默王二"), new ReferenceQueue<>());

mysql的数据引擎有哪些, 区别(innodb,MyISAM,Memory)

MySQL 支持多种存储引擎,常见的有 MyISAM、InnoDB、MEMORY 等。MEMORY 并不常用。

存储引擎

我来做一个表格对比:

功能 InnoDB MyISAM MEMORY
支持事务 Yes No No
支持全文索引 Yes Yes No
支持 B+树索引 Yes Yes Yes
支持哈希索引 Yes No Yes
支持外键 Yes No No

如何切换数据库引擎

可以通过 alter table 语句来切换 MySQL 的数据引擎。

ALTER TABLE your_table_name ENGINE=InnoDB;

不过不建议,应该提前设计好到底用哪一种存储引擎。

mysql一共有哪些锁

三分恶面渣逆袭:MySQL 中的锁

按锁粒度划分的话,MySQL 的锁有:

  • 表锁:开销小,加锁快;锁定力度大,发生锁冲突概率高,并发度最低;不会出现死锁。
  • 行锁:开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高。
  • 页锁:开销和加锁速度介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般。

按兼容性划分的话,MySQL 的锁有:

  • 共享锁(S Lock),也叫读锁(read lock),相互不阻塞。
  • 排他锁(X Lock),也叫写锁(write lock),排它锁是阻塞的,在一定时间内,只有一个请求能执行写入,并阻止其它锁读取正在写入的数据。

说说你对RocketMQ的理解

牧小农:RocketMQ 的作用

RocketMQ 是阿里巴巴开源的一款分布式消息中间件,具有高吞吐量、低延迟和高可用性。其主要组件包括生产者、消费者、Broker、Topic 和队列。消息由生产者发送到 Broker,再根据路由规则存储到队列中,消费者从队列中拉取消息进行处理。适用于异步解耦和流量削峰等场景。

说说死信队列?

死信队列用于存储那些无法被正常处理的消息,这些消息被称为死信(Dead Letter)。

阿里云官方文档:死信队列

产生死信的原因是,消费者在处理消息时发生异常,且达到了最大重试次数。当消费失败的原因排查并解决后,可以重发这些死信消息,让消费者重新消费;如果暂时无法处理,为避免到期后死信消息被删除,可以先将死信消息导出并进行保存。

如何处理消息重复消费的问题?

RocketMQ 可以保证消息一定投递,且不丢失,但无法保证消息不重复消费。

因此,需要在业务端做好消息的幂等性处理,或者做消息去重。

三分恶面渣逆袭:幂等和去重

幂等性是指一个操作可以执行多次而不会产生副作用,即无论执行多少次,结果都是相同的。可以在业务逻辑中加入检查逻辑,确保同一消息多次消费不会产生副作用。

例如,在支付场景下,消费者消费扣款的消息,对一笔订单执行扣款操作,金额为100元。

如果因网络不稳定等原因导致扣款消息重复投递,消费者重复消费了该扣款消息,但最终的业务结果要保证只扣款一次,金额为100元。如果扣款操作是符合要求的,那么就可以认为整个消费过程实现了消息幂等。

消息去重,是指在消费者消费消息之前,先检查一下是否已经消费过这条消息,如果消费过了,就不再消费。

业务端可以通过一个专门的表来记录已经消费过的消息 ID,每次消费消息之前,先查询一下这个表,如果已经存在,就不再消费。

public void processMessage(String messageId, String message) {
    if (!isMessageProcessed(messageId)) {
        // 处理消息
        markMessageAsProcessed(messageId);
    }
}

private boolean isMessageProcessed(String messageId) {
    // 查询去重表,检查消息ID是否存在
}

private void markMessageAsProcessed(String messageId) {
    // 将消息ID插入去重表
}

如何保证幂等性

勇哥:消费幂等

首先,消息必须携带业务唯一标识,可以通过雪花算法生成全局唯一 ID。

Message msg = new Message(TOPIC /* Topic */,
             TAG /* Tag */,
               ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
             );
message.setKey("ORDERID_100"); // 订单编号
SendResult sendResult = producer.send(message);      

其次,在消费者接收到消息后,判断 Redis 中是否存在该业务主键的标志位,若存在标志位,则认为消费成功,否则执行业务逻辑,执行完成后,在缓存中添加标志位。

public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
    try {
        for (MessageExt messageExt : msgs) {
           String bizKey = messageExt.getKeys(); // 唯一业务主键
           //1. 判断是否存在标志
           if(redisTemplate.hasKey(RedisKeyConstants.WAITING_SEND_LOCK + bizKey)) {
            continue;
          }
           //2. 执行业务逻辑
           //TODO do business
           //3. 设置标志位
           redisTemplate.opsForValue().set(RedisKeyConstants.WAITING_SEND_LOCK + bizKey, "1"72, TimeUnit.HOURS);
        }
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    } catch (Exception e) {
        logger.error("consumeMessage error: ", e);
        return ConsumeConcurrentlyStatus.RECONSUME_LATER;
    }
}

然后,利用数据库的唯一索引来防止业务的重复插入。

CREATE TABLE `t_order` (
  `id` bigint(20NOT NULL AUTO_INCREMENT,
  `order_id` varchar(64NOT NULL COMMENT '订单编号',
  `order_name` varchar(64NOT NULL COMMENT '订单名称',
  PRIMARY KEY (`id`),
  UNIQUE KEY `order_id` (`order_id`)
ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

最后,在数据库表中使用版本号,通过乐观锁机制来保证幂等性。每次更新操作时检查版本号是否一致,只有一致时才执行更新并递增版本号。如果版本号不一致,则说明操作已被执行过,拒绝重复操作。

public void updateRecordWithOptimisticLock(int id, String newValue, int expectedVersion) {
    int updatedRows = jdbcTemplate.update(
        "UPDATE records SET value = ?, version = version + 1 WHERE id = ? AND version = ?",
        newValue, id, expectedVersion
    );
    if (updatedRows == 0) {
        throw new OptimisticLockingFailureException("Record has been modified by another transaction");
    }
}

或者悲观锁机制,通过数据库的锁机制来保证幂等性。

public void updateRecordWithPessimisticLock(int id) {
    jdbcTemplate.queryForObject("SELECT * FROM records WHERE id = ? FOR UPDATE", id);
    jdbcTemplate.update("UPDATE records SET value = ? WHERE id = ?""newValue", id);
}

什么是雪花算法?

雪花算法是由 Twitter 开发的一种分布式唯一 ID 生成算法。

技术派教程:雪花算法

雪花算法以 64 bit 来存储组成 ID 的4 个部分:

  1. 最高位占1 bit,始终为 0,表示正数。
  2. 中位占 41 bit,值为毫秒级时间戳;
  3. 中下位占 10 bit,机器 ID(包括数据中心 ID 和机器 ID),可以支持 1024 个节点。
  4. 末位占 12 bit,值为当前毫秒内生成的不同的自增序列,值的上限为 4096;

目前雪花算法的实现比较多,可以直接使用 Hutool 工具类库中的 IdUtil.getSnowflake() 方法来获取雪花 ID。

long id = IdUtil.getSnowflakeNextId();

内容来源

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

ending

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

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

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

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

浏览 10010
24点赞
评论
收藏
分享

手机扫一扫分享

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

手机扫一扫分享

分享
举报