专栏名称: 沉默王二
技术文通俗易懂,吹水文风趣幽默。学 Java,认准二哥的网站 javabetter.cn
目录
相关文章推荐
李楠或kkk  ·  俄美会谈,俄国给了美国什么??? ... ·  昨天  
李楠或kkk  ·  elon musk ... ·  昨天  
新能源汽车评论  ·  誓死不国产的“日系车之王”,16年后终于低头了? ·  昨天  
新能源汽车评论  ·  誓死不国产的“日系车之王”,16年后终于低头了? ·  昨天  
平安宁夏  ·  公安部公布5起证券交易犯罪典型案例 ·  2 天前  
新熵  ·  DeepSeek成BAT生死符,不接入就出局? ·  3 天前  
51好读  ›  专栏  ›  沉默王二

京东年终奖方案出了,值得提前恭喜。

沉默王二  · 公众号  · 科技自媒体  · 2024-12-25 14:04

主要观点总结

本文主要介绍了关于京东年终奖方案、Java面试指南、Spring的bean、AOP相关原理、Redis的数据和缓存不一致处理、分布式锁以及技术派实战项目中遇到的难点等内容。

关键观点总结

关键观点1: 京东年终奖方案

介绍了京东的年终奖结构,包括O序列和P序列的年终奖发放情况,以及京东面试的相关问题。

关键观点2: Java面试指南

提供了关于Java面试的一些重要知识点,包括Spring的bean、AOP原理等。

关键观点3: Spring的bean

解释了Spring的bean是什么,以及如何通过注解方式、XML配置、Java配置三种方式声明bean。还介绍了@Component和@Bean的区别。

关键观点4: AOP相关原理

介绍了AOP的原理,即通过动态代理实现,包括JDK动态代理和CGLIB动态代理。还介绍了AOP在项目中用于日志记录、事务管理等的场景。

关键观点5: Redis的数据和缓存不一致处理

解释了Redis的数据和缓存不一致的问题,以及在实际项目中的处理策略。

关键观点6: 分布式锁

介绍了分布式锁的概念和作用,以及使用Redis实现分布式锁的方式。

关键观点7: 技术派实战项目中遇到的难点

主要介绍了在高并发情况下,如何解决热点文章导致的数据库请求压力问题,以及通过Redisson的看门狗算法解决缓存未命中问题的解决方案。


正文

大家好,我是二哥呀。

京东年终奖方案的出炉,意味着互联网大厂年终奖发放的第一枪已经打响。

讲句良心话,刚参加工作那几年,每年最大的期盼就是年终奖,平常的工资可能没啥感觉,但年终奖总给人一种沉甸甸的感觉。

不得不说,东哥还是有诚意的。

截图来自宫水三叶

我简单给大家拆解一下:

①、O 序列年前就发放,1 月 26 号刚好是春节前的倒数第二天。春节前能拿到一笔奖金,过年的味也能足不少。

O 序列一般代指基层员工,包括快递员、客服等;P 序列一般代指项目管理岗,要到年后 2 月份才能发放。

②、年终奖的结构分为固定+目标,目标能拉开多达 5 个月的月薪,对于大厂的员工来说,这可不是小数目。

  • 绩效 A+:8 倍月薪 → 全年 20 薪(12 + 5 + 3 = 20)
  • 绩效 A:6.5 倍月薪 → 全年 18.5 薪
  • 绩效 B+:5 倍月薪 → 全年 17 薪
  • 绩效 B-:3 倍月薪 → 全年 15 薪

8 倍月薪,意味着京东在 10 月份定下的目标,从 17 薪迈向 20 薪它实现了,采销部门更是要在 2026 年迈向 26 薪的新台阶。

同一个大厂,不同部门真的是天差地别,年终奖就是最好的体现 。京东零售和京东物流拿到 A+ 绩效的肯定是最多的两个部门,因为它们是京东的核心支柱产业。

照这个节奏,京东 25 届开出的 19 薪,基本上是能够落地的,拿到京东 offer 的小伙伴可以把心放到肚子里了。

接下来,我们就以《Java 面试指南》中收录的京东同学 9 技术一面为例,来看看京东的面试官都喜欢问哪些问题,如果打算实习、春招、秋招去京东的话,应该偏重哪个方向。

背八股就认准三分恶的面渣逆袭

京东面经同学 9 技术一面

什么样的对象算作垃圾对象

Java 通过可达性分析算法来判断一个对象是否还存活。

通过一组名为 “GC Roots” 的根对象,进行递归扫“”无法从根对象到达的对象就是“垃圾”,可以被回收。

三分恶面渣逆袭:GC Root

问了垃圾回收算法,针对问了每个算法的优缺点

垃圾收集算法主要有三种,分别是标记-清除算法、标记-复制算法和标记-整理算法。

说说标记-清除算法?

标记-清除 算法分为两个阶段:

  • 标记 :标记所有需要回收的对象
  • 清除 :回收所有被标记的对象
三分恶面渣逆袭:标记-清除算法

优点是实现简单,缺点是回收过程中会产生内存碎片。

说说标记-复制算法?

标记-复制 算法可以解决标记-清除算法的内存碎片问题,因为它将内存空间划分为两块,每次只使用其中一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后清理掉这一块。

三分恶面渣逆袭:标记-复制算法

缺点是浪费了一半的内存空间。

说说标记-整理算法?

标记-整理 算法是标记-清除复制算法的升级版,它不再划分内存空间,而是将存活的对象向内存的一端移动,然后清理边界以外的内存。

标记-整理算法

缺点是移动对象的成本比较高。

问了CMS垃圾回收器

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

CMS 使用 标记-清除 算法进行垃圾收集,分 4 大步:

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

标记复制的标记过程和复制过程会不会停顿?

在标记-复制算法 中,标记阶段和复制阶段都会触发STW。

  • 标记阶段停顿是为了保证对象的引用关系不被修改。
  • 复制阶段停顿是防止对象在复制过程中被修改。

怎么理解并发和并行,然后顺势引申到并发垃圾回收器和并行垃圾回收器是怎么做到的:举了Parallel Old和CMS来问

  • 并行:多核 CPU 上的多任务处理,多个任务在同一时间真正地同时执行。
  • 并发:单核 CPU 上的多任务处理,多个任务在同一时间段内交替执行,通过时间片轮转实现交替执行。
三分恶面渣逆袭:并行和并发

就好像我们去食堂打饭,并行就是每个人对应一个阿姨,同时打饭;而并发就是一个阿姨,轮流给每个人打饭。

三分恶面渣逆袭:并行并发和食堂打饭

说说 Parallel Old 收集器?

Parallel Old 是 Parallel Scavenge 收集器的老年代版本,基于标记-整理算法实现,使用多条 GC 线程在 STW 期间同时进行垃圾回收。

三分恶面渣逆袭:Parallel Old收集器

说说 CMS 收集器?

CMS 在 JDK 1.5 时引入,JDK 9 时被标记弃用,JDK 14 时被移除。优点是垃圾回收线程和应用线程同时运行,停顿时间短,适合延迟敏感的应用,但容易产生内存碎片,可能触发 Full GC。

小潘:CMS

线程池的工作原理?

当应用程序提交一个任务时,线程池会根据当前线程的状态和参数决定如何处理这个任务。

  • 如果线程池中的核心线程都在忙,并且线程池未达到最大线程数,新提交的任务会被放入队列中进行等待。
  • 如果任务队列已满,且当前线程数量小于最大线程数,线程池会创建新的线程来处理任务。

空闲的线程会从任务队列中取出任务来执行,当任务执行完毕后,线程并不会立即销毁,而是继续保持在池中等待下一个任务。

当线程空闲时间超出指定时间,且当前线程数量大于核心线程数时,线程会被回收。

三分恶面渣逆袭:线程池执行流程

什么时候会执行拒绝策略?

当线程池无法接受新的任务时,也就是线程数达到 maximumPoolSize,任务队列也满了的时候,就会触发拒绝策略。

场景题:假如要查10万多条数据,用线程池分成20个线程去执行,怎么做到等所有的线程都查找完之后,即最后一条结果查找结束了,才输出结果?

为每个线程创建一个任务,使用 CountDownLatch 计数器控制线程同步。

每个线程任务完成后调用 countDown() ,主线程使用 await() 等待所有线程完成。

class DataQueryExample {

    public static void main(String[] args) throws InterruptedException {
        // 模拟10万条数据
        int totalRecords = 100000;
        int threadCount = 20;
        int batchSize = totalRecords / threadCount; // 每个线程处理的数据量

        // 创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
        CountDownLatch latch = new CountDownLatch(threadCount);

        // 模拟查询结果
        ConcurrentLinkedQueue results = new ConcurrentLinkedQueue<>();

        for (int i = 0; i             int start = i * batchSize;
            int end = (i == threadCount - 1) ? totalRecords : (start + batchSize);
            
            executor.execute(() -> {
                try {
                    // 模拟查询操作
                    for (int j = start; j                         results.add("Data-" + j);
                    }
                    System.out.println(Thread.currentThread().getName() + " 处理数据 " + start + " - " + end);
                } finally {
                    latch.countDown(); // 线程任务完成,计数器减1
                }
            });
        }

        // 等待所有线程完成
        latch.await();
        executor.shutdown();

        // 输出结果
        System.out.println("所有线程执行完毕,查询结果总数:" + results.size());
    }
}

怎么理解spring的bean

Bean 是指由 Spring 容器管理的对象,它的生命周期由容器控制,包括创建、初始化、使用和销毁。以通过三种方式声明: 注解方式 XML 配置 Java 配置

二哥的 Java 进阶之路:Bean 的声明方式

@Component 和 @Bean 的区别

@Component 是 Spring 提供的一个类级别注解,由 Spring 自动扫描并注册到 Spring 容器中。

@Bean 是一个方法级别的注解,用于显式地声明一个 Bean,当我们需要第三方库或者无法使用 @Component 注解类时,可以使用 @Bean 来将其实例注册到容器中。

@Configuration
public class AppConfig {
    @Bean
    public UserService userService() {
        return new UserService();
    }
}

依赖注入的时候,直接Autowired比较直接,为什么推荐构造方法注入呢

当使用 @Autowired 注解注入 Bean 时,IDEA 会提示“Field injection is not recommended”。

二哥的 Java 进阶之路:@Autowired

这是因为字段注入的方式:

  • 不能像构造方法那样使用 final 注入不可变对象
  • 隐藏了依赖关系,调用者可以看到构造方法注入或者 setter 注入,但无法看到私有字段的注入

在 Spring 4.3 及更高版本中,如果一个类只有一个构造方法,Spring 会自动使用该构造方法进行依赖注入,无需使用 @Autowired 注解。

技术派:构造方法注入

提到了byType,如果两个类型一致的发生了冲突,应该怎么处理

当容器中存在多个相同类型的 bean,编译器会提示 Could not autowire. There is more than one bean of 'UserRepository2' type.

@Component
public class UserRepository21 implements UserRepository2 {}

@Component
public class UserRepository22 implements UserRepository2 {}

@Component
public class UserService2 {
    @Autowired
    private UserRepository2 userRepository; // 冲突
}

这时候,就可以配合 @Qualifier 注解来指定具体的 bean 名称:

@Component("userRepository21")
public class UserRepository21 implements UserRepository2 {
}
@Component("userRepository22")
public class UserRepository22 implements UserRepository2 {
}
@Autowired
@Qualifier("userRepository22")
private UserRepository2 userRepository22;

或者使用 @Resource 注解按名称进行注入,指定 name 属性。

@Resource(name = "userRepository21")
private UserRepository2 userRepository21;

AOP相关原理说一下

AOP 是通过 动态代理 实现的,代理方式有两种:JDK 动态代理和 CGLIB 代理。

①、JDK 动态代理是基于接口的代理,只能代理实现了接口的类。

②、CGLIB 动态代理是基于继承的代理,可以代理没有实现接口的类。







请到「今天看啥」查看全文