likes
comments
collection
share

🚀💥最新对线面试官,线下试一试,干!!!

作者站长头像
站长
· 阅读数 26

运动确实会带来运气和机会

🚀💥最新对线面试官,线下试一试,干!!! 昨天晚上:晚上出去跑了跑步,突然手机一顿颤抖。接通后发现,原来有人事发现了我,邀请我去面试,询问我什么时间方便线下。面对着寒境下为数不多的面试机会,我当即决定明天下午就去。。。

今天:提前半个小时来到了大楼下,转了好几圈,终于找到了入口。

公司内:看着公司装修还不错,进入面试间等待,半个小时后,一个眼看着就颇具实力的面试官(事后发现是前某度的)进来了,我笑脸相迎,开始了愉快的对线。。。

现场面试提问记录

  1. static关键字?
  2. 单例模式懒加载?会有什么问题?为什么要static修饰成员变量和方法?如何控制并发?为什么要二次判断是否创建成功了?锁定的粒度?锁是重量级操作,使用锁合理吗?
  3. concurrentHashMap?
  4. 线程CountDownLatch,信号量?
  5. volatile?跟cpu有关系吗?跟一级缓存、二级缓存有关系吗?
  6. spring依赖注入,有哪些方案?
  7. 每个注解的出现是解决了什么问题?比如,这样的类的关系如何实现:A里面引用B,A‘继承了A,B’是B的子类,现在要实现A‘调用B’的实现方法?
  8. redis-cluster模式下,如果需要同时原子的执行三条指令才可以完成业务,如何做?如果用lua包裹,针对修改的数据不在一个分片时,如何处理?
  9. 你们的系统高Qps方案,会如何设计?

思考复盘

spring依赖注入

Spring 依赖注入的多种实现方式

在 Spring 框架中,依赖注入(Dependency Injection, DI)是一个核心概念,旨在通过外部注入的方式来管理对象之间的依赖关系,从而降低耦合性并提高代码的可测试性。本文将介绍各种依赖注入的方式,并实现一个具体的类关系:A 里面引用 BA' 继承了 AB'B 的子类,现在要实现 A' 调用 B' 的实现方法。

类定义和关系

假设我们有以下类关系:

  • A 引用了 B
  • A' 继承了 A
  • B'B 的子类

并且我们希望在 A' 中调用 B' 的方法。

示例类

package com.kabai;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;

// 定义类 B
@Component
public class B {
    public void doSomething() {
        System.out.println("B is doing something");
    }
}

// 定义类 BPrime 继承 B
@Component
public class BPrime extends B {
    @Override
    public void doSomething() {
        System.out.println("BPrime is doing something");
    }
}

// 定义类 A
@Component
public class A {
    protected B b;

    @Autowired
    public void setB(B b) {
        this.b = b;
    }

    public void execute() {
        b.doSomething();
    }
}

// 定义类 APrime 继承 A
@Component
public class APrime extends A {
    private BPrime bPrime;

    @Autowired
    @Qualifier("bPrime")
    public void setBPrime(BPrime bPrime) {
        this.bPrime = bPrime;
    }

    @Override
    public void execute() {
        bPrime.doSomething();
    }
}

1. 使用注解

1.1 @Autowired

@Autowired 注解用于自动注入 Spring 容器中的 bean。它可以应用于构造函数、方法、属性或参数上,通过类型匹配的方式自动注入依赖。

@Component
public class A {
    @Autowired
    protected B b;

    public void execute() {
        b.doSomething();
    }
}

@Component
public class APrime extends A {
    @Autowired
    @Qualifier("bPrime")
    private BPrime bPrime;

    @Override
    public void execute() {
        bPrime.doSomething();
    }
}

1.2 @Resource

@Resource 注解是 Java EE 提供的注解,可以按名称或类型注入 bean。

@Component
public class A {
    @Resource(name = "b")
    protected B b;

    public void execute() {
        b.doSomething();
    }
}

@Component
public class APrime extends A {
    @Resource(name = "bPrime")
    private BPrime bPrime;

    @Override
    public void execute() {
        bPrime.doSomething();
    }
}

2. 使用 XML 配置

XML 配置是一种较为传统的方式,通过 XML 文件来配置 bean 以及它们之间的依赖关系。

<!-- applicationContext.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="b" class="com.kabai.B"/>
    <bean id="bPrime" class="com.kabai.BPrime"/>

    <bean id="a" class="com.kabai.A">
        <property name="b" ref="b"/>
    </bean>

    <bean id="aPrime" class="com.kabai.APrime">
        <property name="b" ref="bPrime"/>
    </bean>
</beans>

3. 使用 Java 配置类

使用 Java 配置类,可以通过 @Configuration@Bean 注解来定义和配置 bean。

@Configuration
@ComponentScan(basePackages = "com.kabai")
public class AppConfig {

    @Bean
    public B b() {
        return new B();
    }

    @Bean
    public BPrime bPrime() {
        return new BPrime();
    }

    @Bean
    public A a() {
        A a = new A();
        a.setB(b());
        return a;
    }

    @Bean
    public APrime aPrime() {
        APrime aPrime = new APrime();
        aPrime.setB(bPrime());
        return aPrime;
    }
}

4. 构造函数注入

构造函数注入通过类的构造函数来注入依赖,这种方式在实例化时强制依赖的存在。

@Component
public class A {
    protected B b;

    @Autowired
    public A(B b) {
        this.b = b;
    }

    public void execute() {
        b.doSomething();
    }
}

@Component
public class APrime extends A {
    private BPrime bPrime;

    @Autowired
    public APrime(BPrime bPrime) {
        super(bPrime);
        this.bPrime = bPrime;
    }

    @Override
    public void execute() {
        bPrime.doSomething();
    }
}

5. Setter 方法注入

Setter 方法注入通过类的 Setter 方法来注入依赖。

@Component
public class A {
    protected B b;

    @Autowired
    public void setB(B b) {
        this.b = b;
    }

    public void execute() {
        b.doSomething();
    }
}

@Component
public class APrime extends A {
    private BPrime bPrime;

    @Autowired
    @Override
    public void setB(B b) {
        if (b instanceof BPrime) {
            this.bPrime = (BPrime) b;
        }
        super.setB(b);
    }

    @Override
    public void execute() {
        bPrime.doSomething();
    }
}

总结

Spring 提供了多种依赖注入的方式,包括注解、XML 配置、Java 配置类、构造函数注入和 Setter 方法注入。

redis-cluster模式下,如果需要同时原子的执行三条指令

在 Redis 集群模式下,同时原子地执行三条指令是一项挑战,特别是当涉及到多个分片时。Lua 脚本可以用来确保在单个节点上的原子性操作,但在跨节点时则需要额外的处理。以下是一些方法和考虑:

单节点原子操作

如果所有涉及的键都位于同一个分片(同一个 Redis 节点上),那么可以使用 Lua 脚本来确保操作的原子性:

redis.call('SET', 'key1', 'value1')
redis.call('SET', 'key2', 'value2')
redis.call('SET', 'key3', 'value3')

跨节点操作

在 Redis 集群模式下,涉及多个分片的情况,需要一些复杂的机制来确保操作的原子性。可以考虑以下方法:

  1. 在应用层实现分布式锁

    • 在每个涉及的分片上加锁,以确保这些操作可以顺序执行。可以使用 SETNXPEXPIRE 来实现分布式锁。
  2. 使用事务机制

    • 如果 Redis 集群节点支持事务,可以考虑在应用层将操作拆分成多个事务,并通过幂等性和重试机制来确保操作的一致性。
  3. 分布式事务协调器

    • 引入一个分布式事务协调器,如 XA 或 TCC(Try-Confirm-Cancel)模式。这种方法适用于对事务一致性要求极高的场景,但实现起来较为复杂。

示例:使用 Lua 脚本和分布式锁

  1. 加锁

    为每个涉及的键加锁:

    -- Lua script for acquiring locks
    local lock1 = redis.call('SETNX', 'lock:key1', 'lock_value')
    local lock2 = redis.call('SETNX', 'lock:key2', 'lock_value')
    local lock3 = redis.call('SETNX', 'lock:key3', 'lock_value')
    
    if lock1 == 1 and lock2 == 1 and lock3 == 1 then
        -- All locks acquired
        redis.call('PEXPIRE', 'lock:key1', 10000)
        redis.call('PEXPIRE', 'lock:key2', 10000)
        redis.call('PEXPIRE', 'lock:key3', 10000)
        return true
    else
        -- Release any acquired locks
        if lock1 == 1 then redis.call('DEL', 'lock:key1') end
        if lock2 == 1 then redis.call('DEL', 'lock:key2') end
        if lock3 == 1 then redis.call('DEL', 'lock:key3') end
        return false
    end
    
  2. 执行操作

    在应用层检查锁定成功后执行操作,然后释放锁:

    if acquire_locks() then
        redis.call('SET', 'key1', 'value1')
        redis.call('SET', 'key2', 'value2')
        redis.call('SET', 'key3', 'value3')
        release_locks()
    else
        -- Retry or handle failure
    end
    
  3. 释放锁

    -- Lua script for releasing locks
    redis.call('DEL', 'lock:key1')
    redis.call('DEL', 'lock:key2')
    redis.call('DEL', 'lock:key3')
    

总结

通过使用 Lua 脚本、分布式锁和事务协调机制,可以在 Redis 集群模式下实现跨分片的原子性操作。然而,由于实现的复杂性,通常需要根据具体业务需求和场景权衡性能和一致性。

转载自:https://juejin.cn/post/7387303384082677787
评论
请登录