摇人!今年我们新招16000人
共 10567字,需浏览 22分钟
·
2024-07-27 14:04
大家好,我是二哥呀。
就在昨天,京东官媒宣布,京东总部 1 号园区 DEF 三座新楼正式启用,并且新招 16000 人,于 8 月 1 日 正式开启 25 届秋招正式批通道!
好家伙,这比去年早了 20 多天啊,去年是 8 月 21 日才开启。并且今年的 HC 比去年直接多了 1000,这可不是小数目啊!
京东可能排不到第一梯队的互联网大厂,比如说字节、腾讯、阿里,但排到第二梯队肯定是毫无疑问的,并且京东的薪资待遇真的可以。
24 届秋招的时候,我就统计过一波京东的薪资待遇,比如说京东 Java 后端本科 211 就能拿到 23*16 的年包,说实话,一年 37 万左右也是人中龙凤了。
应该有不少小伙伴对京东感兴趣,那接下来我们就通过《Java 面试指南》里收录的《京东面经同学4 云实习》为例,来看看京东面试官都喜欢问哪些问题,好做去新盖的三座大楼体验一下宽敞高效的办公场所(顺带吸一丢丢甲醛😂)。
京东同学4云实习面经
之前面经中重复出现的题目这次就略过了,大家可以去三分恶面渣逆袭在线版查看,我已经帮大家标记好了:https://javabetter.cn/sidebar/sanfene/nixi.html
hashmap是会死锁的, 你知道吗
HashMap 不是线程安全的,多线程下扩容会死循环。因为 JDK1.7 中的 HashMap 使用的是头插法插入元素,在多线程的环境下,扩容的时候就有可能导致出现环形链表,造成死循环。
不过,JDK 8 时已经修复了这个问题,扩容时会保持链表原来的顺序。
i++是原子操作吗?
原子操作指的是一个操作是不可分割的,要么全部执行成功,要么完全不执行。
i++ 不是一个原子操作,它包括三个步骤:
-
从内存中读取 i 的值。 -
对 i 进行加 1 操作。 -
将新的值写入内存。
假如两个线程同时对 i 进行 i++ 操作时,可能会发生以下情况:
-
线程 A 读取 i 的值(假设 i 的初始值为 1)。 -
线程 B 也读取 i 的值(值仍然是 1)。 -
线程 A 将 i 增加到 2,并将其写回内存。 -
线程 B 也将 i 增加到 2,并将其写回内存。
尽管进行了两次递增操作,i 的值只增加了 1 而不是 2。可以使用 synchronized 或 AtomicInteger 确保操作的原子性。
常见的7个GC回收器
就目前来说,JVM 的垃圾收集器主要分为两大类:分代收集器和分区收集器,分代收集器的代表是 CMS,分区收集器的代表是 G1 和 ZGC。
四个引用(强软弱虚)
强引用是 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 的锁有:
-
共享锁(S Lock),也叫读锁(read lock),相互不阻塞。 -
排他锁(X Lock),也叫写锁(write lock),排它锁是阻塞的,在一定时间内,只有一个请求能执行写入,并阻止其它锁读取正在写入的数据。
说说你对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(20) NOT NULL AUTO_INCREMENT,
`order_id` varchar(64) NOT NULL COMMENT '订单编号',
`order_name` varchar(64) NOT 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 bit,始终为 0,表示正数。 -
中位占 41 bit,值为毫秒级时间戳; -
中下位占 10 bit,机器 ID(包括数据中心 ID 和机器 ID),可以支持 1024 个节点。 -
末位占 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 刷题的私密圈子,你可以阅读星球专栏、向二哥提问、帮你制定学习计划、和球友一起打卡成长。
两个置顶帖「球友必看」和「知识图谱」里已经沉淀了非常多优质的学习资源,相信能帮助你走的更快、更稳、更远。
欢迎点击左下角阅读原文了解二哥的编程星球,这可能是你学习求职路上最有含金量的一次点击。
最后,把二哥的座右铭送给大家:没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟。共勉 💪。