实战实战实战

阿里

1、SpringBoot与Spring区别
2、Aop应用
3、服务治理
4、微服务划分
5、微服务调用
6、10个线程实现10000个数的相加
// 使用countDownLatch
public class CalculateThread {
    private int[] sum = new int[10];
    public void calculate(int start, int sumIndex, CountDownLatch countDownLatch) {
        for (int i = start; i< start+100; i++) {
            sum[sumIndex] += i;
        }
        countDownLatch.countDown();
    }
    public int getSum() {
        int ret = 0;
        for (int i =0; i<sum.length; i++) {
            ret+= sum[i];
        }
        return ret;
    }
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(10);
        CalculateThread calculateThread = new CalculateThread();
        for (int i = 0;i < 10; i++) {
            final int j = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    calculateThread.calculate(j * 100, j, countDownLatch);
                }
            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(calculateThread.getSum());
    }
}
8、两个不同长度的链表相交,求交点
  • A长度为 a, B长度为b, 假设存在交叉点,此时 A到交叉点距离为 c, 而B到交叉点距离为d
  • 后续交叉后长度是一样的,那么就是 a-c = b-d -> a+d = b+c
  • 这里意味着只要分别让A和B额外多走一遍B和A,那么必然会走到交叉,最后为交叉点为空说明两个没交叉,最后交叉点不为空则说明有交叉
    在这里插入图片描述
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode currentA = headA;
        ListNode currentB = headB;
        while (currentA != currentB) {
           currentA = currentA == null ? headB : currentA.next;
            currentB = currentB == null ? headA : currentB.next;
        }
        return currentA; 
    }
}

快手

1、静态内部类和内部类的区别
  • 可以对比静态属性和非静态属性来理解
  • 内部类
    • 内部类可以无条件访问外部类的属性和方法(包括私有的),外部类在访问内部属性和方法时必须先创建一个实例
    • 创建内部类实例的时候必须先创建一个外部类实例
    • 内部类不能定义static元素和方法,但是允许static final常量
  • static 内部类
    • 是内部类的一个特例,一旦内部类被static修饰则该类变成了顶层类,只能访问外部类的静态属性和方法
    • 可以直接创建静态内部类的实例
    • 可以在内部定义static元素
2、LinkedList的底层实现是啥, ArrayList的底层实现,两者各自优缺点
  • linkedList底层实现是双向链表,内部主要维护first和last两个节点,非哨兵节点,插入删除效率高,索引方式查找时会判断index是否大于链表长度的一半,如果大于从后向前找,如果小于从0向后找
  • ArrayList底层是一个数组,默认初始化大小是10,当添加元素达到数组现有大小后会按1.5倍来扩容,并拷贝原来的数据
  • linkedList插入和删除元素效率很高,只需要修改链表元素的指针即可,而ArrayList需要整体移动元素,效率很低
  • ArrayList按索引查询时效率比LinkedList高
3、MySql为什么习惯用自增序列作为主键

innoDB存储引擎在选择聚簇索引时的顺序是这样的:如果有主键选用主键,如果没有选用唯一且非空的字段作为聚簇索引,如果都没有则自己生成一个Row_ID作为聚簇索引,使用自增序列不使用uuid是这样考虑的:

  • uuid是一般字符串比较长,比较占用空间
  • 自增序列插入效率高:插入数据可以顺序写入;如果不是自增的,则可能导致大量页分裂和页移动,还有可能写入的目标页已经写入到磁盘中而不仅仅是在内存中,又或者目标页还没有被加载到内存中,这样会导致IO操作,效率低下
4、手写一个阻塞队列
// 参考:https://zhuanlan.zhihu.com/p/64156753
public class BlockQueue {
    private List<String> dataContainer;
    private int size;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    public BlockQueue(int size) {
        this. size = size;
        dataContainer = new LinkedList<>();
    }
    public void put(String element) {
        lock.lock();
        try {
            while (dataContainer.size() >= size) {
                condition.await();
            }
            dataContainer.add(element);
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public String pop() {
        lock.lock();
        String element = null;
        try {
            while (dataContainer.size() == 0) {
                condition.await();
            }
            element = dataContainer.get(0);
            dataContainer.remove(0);
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        finally {
            lock.unlock();
        }
        return element;
    }
}
4、谈谈你对泛型的理解
  • 泛型即参数化类型, 分为 泛型类、泛型方法、泛型接口, 泛型类型变量不能是基本数据类型,因为擦除后是以Object代替原来的类型T的
  • 泛型只在编译阶段有效,编译时先检查代码中泛型的类型,编译之后程序会擦除泛型,泛型信息不会进入到运行时阶段,因此我们可以利用反射来给我们定义好的list添加任意类型的元素
     public static void main(String args[]) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            ArrayList<Integer> array=new ArrayList<Integer>();
            // 这样调用add方法只能存储整形,因为泛型类型的实例为Integer
            array.add(1);
            // 通过泛型可以突破泛型类型约束
            array.getClass().getMethod("add", Object.class).invoke(array, "asd");
            for (int i=0;i<array.size();i++) {
                System.out.println(array.get(i));
                // 如果这里这样定义, 会报类型转换的错误,编译器在这里自动插入int的强制类型转换
                // int a = array.get(i);
            }
      // 这段代码不会报任何的错
     }
    
  • PECS原则(Producer Extends Consumer Super)
    • 频繁往外读取内容的,适合用上界Extends。
    • 经常往里插入的,适合用下界Super。
  • 参考文章:
    • https://blog.csdn.net/sunxianghuang/article/details/51982979
    • https://segmentfault.com/a/1190000005179142

京东

1、10亿条数据怎么排序,以及10亿条数据找出TOPN的数据
  • 10亿条数据排序:
2、对分布式事务的理解
3、谈谈对spring的ioc和AOP的理解
4、volatile的底层实现
5、redis的缓存击穿、缓存雪崩怎么解决
6、秒杀系统应该考虑哪些问题
7、spring中的事务是个什么样子的
8、Redis单线程为什么这么快
  • Redis是内存数据库,操作内存比IO本身就快
  • 单线程可以避免线程之间的切换耗费时间,而且Redis本身耗时的动作是在IO操作上,高版本的Redis的IO是多线程的
9、Redis持久化的方式和每种方式的优缺点
10、2.X版本后Redis不再使用zookeper作为注册中心的优缺点
11、常见排序算法及其稳定性
  • 排序稳定性:对于任意两个相等的元素排序前和排序后的相对顺序不变,称为该算法是稳定的
  • 稳定排序:插入排序、冒泡排序、归并排序(nlogn)
  • 非稳定排序:快排(最坏n方,最好nlogn)、堆排(nlogn)
12、水平触发和边缘触发
13、负载比较高怎么排查
14、CAS一般的应用场景

小红书

1、Redis大的key怎么删除
2、Redis某个热key访问量达到 8 9万以上怎么解
3、MySql的索引覆盖和索引下推
  • 索引覆盖:在一棵B+Tree上就可以获得SQL所需的所有列的数据:使用联合索引替换单列索引,可以避免回表
  • 索引下推:没有索引下推优化的时候,当进行索引查询时,先根据索引来查找记录,然后回表根据where条件过滤;当有索引下推优化的时候,在根据索引条件查询的时候(一般是联合索引)如果可以就直接进行where条件过滤了,减少回表次数
  • 参考:https://www.jianshu.com/p/d0d3de6832b9
4、MySql的幻读和可重复读的怎么解决
  • 可重复读:主要是靠MVCC来实现的:undolog + ReadView + 3个隐式字段
  • 幻读:主要是靠临建锁来解决的
5、线程池各个参数的意义,以及等待队列使用LinkedBlockQueue和ArrayBlockedQueue的应用场景
  • 核心线程数、最大线程数、keepAliveTime, 阻塞队列
  • keepAliveTime 说的是非核心线程数处于空闲时间后会被回收,核心线程也可以被回收,但是要设置 allowCoreThreadTimeOut
  • 阻塞队列的设置
    • ArrayBlockingQueue: 有界缓存等待队列,可以指定大小,当正在执行的线程数等于核心线程数的时候
    • LinkedBlockingQueue:如果不指名大小是无界缓存队列,当执行任务的线程数量达到核心线程数后,剩下的任务会放在无界阻塞队列中,此时最大线程数是无效的
  • 关闭线程池的2种方法:
    • shutdown: 不再接收新的任务,但是线程池中的任务会执行完
    • shutdownNow:杀掉线程池中正在执行的任务,并返回尚未执行的任务列表

金山云

1、熔断、降级、限流
  • 熔断:当服务A调用下游服务B的时候,B由于某些原因接口没响应,导致A服务调用接口一直超时,当超时次数达到一定阈值后A停止调用B的接口,直接使用MOCK的数据,熔断器有阿里的Sentinel或者hystrix
  • 限流:当A大并发调用B的时候,B可以限制A的流量上限,同一时间设定固定的线程数量来支持A的调用
  • 降级:保住核心业务,将非核心业务关闭或者简化掉
  • 参考: https://www.cnblogs.com/qianjinyan/p/10816952.html
2、大表怎么添加索引
  • 如果是主从的话,可以先把从机停掉,先给从机上添加索引,然后把从机起来,主从切换,然后再在原来的主机上添加索引
  • 如果只有一个实例的话:找业务不忙的时候添加索引
3、Spring的事务
4、什么情况下会线程不安全
5、hashMap什么情况下会线程不安全
6、String从一个json中读取是放到常量池中么
7、百万数据分策略怎么分
8、 Redis的Zset的底层实现原理
9、水平分表的底层实现原理
10、http包里面都包含什么
11、怎么看某个服务当前的连接状态
12、G1垃圾回收器的安全点

百度

1、为什么要分代

优化GC性能, 假如不分代的话,每次GC都要全堆扫描,这样效率很低;大部门的对象的生命周期很短,当使用分代模型之后把朝生夕死的对象放到某一个地方,发生GC后就可以腾出很大的空间。

2、kafka的使用场景

削峰填谷、服务之间进行解耦、方便扩展应用

3、redis常见数据结构底层的实现
  • String: 使用SDS来实现的,简单动态Str,其实就是一个结构体,然后里面有char型数组,还有标志数组存储的元素的个数的变量、还有数组的未使用的字节数量
  • List:底层实现是一个双向链表
  • 参考:https://www.cnblogs.com/ysocean/p/9080942.html
4、大对象为什么直接放到老年代
  • 如果放到年轻代的话,来回复制会消耗很多的时间(对象大 + GC频率高)
  • 放到年轻代首先会加快Edgen区GC的频率,假如大对象躲过了GC放到了surivor区,surivor区本来就小,这样surivor能放的对象就变少了,迫使对象放到老年代,这样就会加快老年代的GC
5、JDK目前最新的版本是什么

JDK16、JDK9垃圾回收器默认使用的是G1

滴滴

1、hashMap的底层实现
  • hashMap底层是数组加链表来实现的,JDK1.8中当链表长度达到阈值8之后会变成红黑树
  • 各个参数值:装载因子:默认0.75;数组初始化大小:16;扩容时是以2倍原来容量来扩容的
  • jdk1.7头插法出现环形链表:
  • 出现环形链表主要是在transfer方法中,参考:https://xw.qq.com/cmsid/20200825A0O09T00
  • hashMap在JDK1.8中做了哪些优化
    • 1.7是使用数组+单向链表, 1.8是用的是数组+单向链表+红黑树,链表长度达到阈值8时转化成红黑树
    • 计算Hash值的时候,1.8中hashcode的高16位参与了运算,异或hashcode
    • 1.7使用的是头插法,1.8使用的是尾插法,1.7并发情况下容易出现循环链表
    • 1.7中计算索引位置时用的是hashCode & length-1, 1.8用的是扩容前的位置+扩容的大小值
    • 1.7 是先扩容后插入, 1.8是先插入后扩容
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值