准备下度小满的面试


synchronized和reentrantlock

synchronized为非公平锁, ReentrantLock则即可以选公平锁也可以选非公平锁。synchronzied锁的是对象,锁是保存在对象头里面的,根据对象头数据来标识是否有线程获得锁/争抢锁;ReentrantLock锁的是线程,根据进入的线程和int类型的state标识锁的获得/争抢。

ThreadLocal

ThreadLocal子线程会有这个数据吗

不能,ThreadLocal是当前调用线程的本地变量,二者不能共享。但是有途径可以做到:InheritableThreadLocal。会在子线程初始化的时候将线程的ThreadLocalMap传给子线程。

线程池ThreadLocal

线程池会重用线程,可能会从ThreadLocal获取到其他用户遗留的值。

所以在使用的事件后,需要在代码运行完之后,手动清空设置的值。

ThreadLocal内存泄漏

由于ThreadLocalMap的key是弱引用,而Value是强引用。这就导致了一个问题,ThreadLocal在没有外部对象强引用时,发生GC时弱引用Key会被回收,而Value不会回收,如果创建ThreadLocal的线程一直持续运行,那么这个Entry对象中的value就有可能一直得不到回收,发生内存泄露。

在ThreadLocal的get()、set()、remove()方法调用的时候会清除掉线程ThreadLocalMap中所有Entry中Key为null的Value,并将整个Entry设置为null,利于下次内存。

四种隔离级别(ACID中I)

可重复读

第一次读取到数据之后,就会将数据加锁,其他事务无法修改这些数据。

但是这种方法无法锁住insert的数据,如果A进行范围查询,事务B插入了数据并且提交了,A再次范围查询就会读到之前没有的数据,就是幻读。间隙锁。

mvcc解决了快照读下的幻读问题(select),但是他无法解决当前读下的幻读问题(select for update),当前读下的幻读要通过行锁+间隙锁来解决。

MVCC:将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

MVCC会将不同事务对同一条记录的修改记录成一个线性表。快照读都是读取最新的记录。

Redo log

记录了数据操作在屋里层面的修改,MySQL大量使用缓存,修改操作时会直接修改内存,而不是修改磁盘。事务运行中会不断产生redo log,在事务提交的时候刷盘。顺序写入,磁盘顺序读速度远大于随机读。当数据库重启时,会根据redo log进行数据的恢复。

SpringBoot自动装配原理

SpringBoot在启动时会扫描外部引用jar包的META-INF/sprint.factories文件,将文件中的配置信息加载到Spring容器,并执行类中定义的各种操作。对于外部jar来说,只需按照SpringBoot定义的标准,就能将功能装置进SpringBoot。

通过注解或者一些简单的配置就能在 Spring Boot 的帮助下实现某块功能。

@SpringBootApplication注解:

  • @EnableAutoConfiguration:开启自动配置机制,实现自动装配的核心注解
  • @Configuration:允许在上下文中注册额外的bean导入其他配置类。
  • @ComponentScan:扫描被@Service@Controller注解的bean。

自动装配核心功能的实现实际是通过 AutoConfigurationImportSelector类。

Spring Boot 通过@EnableAutoConfiguration开启自动装配,通过 SpringFactoriesLoader 最终加载META-INF/spring.factories中的自动配置类实现自动装配,自动配置类其实就是通过@Conditional按需加载的配置类,想要其生效必须引入spring-boot-starter-xxx包实现起步依赖

redis的哈希表怎么rehash的

redis字典底层有两个数组,还有一个rehashidx控制rehash。

当元素个数和hash表长度一致时,就会发生扩容,长度变为原来的两倍。

单步rehash,每次增删改查,除了完成指定的操作之外,rehashidx+1,然后执行对应原hash表rehashidx索引位置的rehash。

分治的思想,避免集中rehash带来的庞大计算量影响正常服务。

此过程中,如果增删改查,index大于rehashidx,需要访问原数组,否则是新数组。

写个小顶堆排序

首先,建堆,从n/2往0建,复杂度O(n)。然后把0位置换到当前数组末尾。往下整理。过程中可能出现的错误点:数组当前长度管理上。

public class Main {
    public static int sz;
    public static void swap(int[] nums,int x,int y) {
        int tmp = nums[x];
        nums[x] = nums[y];
        nums[y] = tmp;
    }
    public static void buildHeap(int[] nums) {
        int n = nums.length;
        for(int i=n/2;i>=0;i--) {
            tidyHeap(nums,i);
        }
    }

    public static void tidyHeap(int[] nums,int root) {
        int left = root*2+1;
        int right = root*2+2;
        if(left>=sz-1) return;
        if(right>=sz-1) {
            if(nums[root]>nums[left]) {
                swap(nums,root,left);
                tidyHeap(nums,left);
            }
            return;
        }
        if(nums[right]<=nums[left]&&nums[right]<nums[root]) {
            swap(nums,root,right);
            tidyHeap(nums,right);
            return;
        }
        if(nums[left]<nums[right]&&nums[left]<nums[root]) {
            swap(nums,root,left);
            tidyHeap(nums,left);
        }
    }

    public static int popHeap(int[] nums) {
        int ret = nums[0];
        swap(nums,0,sz-1);
        tidyHeap(nums,0);
        sz--;
        return ret;
    }
    public static List<Integer> heapSort(int[] nums) {
        sz = nums.length;
        buildHeap(nums);
        List<Integer> list = new ArrayList<>();
        for(int i=0;i<nums.length;i++) {
            list.add(popHeap(nums));
        }
        return list;
    }
    public static void main(String[] args) {
        int[] nums = new int[]{3,2,1,0,3,2,3,4,5,6,7,8,9,8,7,6,5,4,3,2,1,2,3,4,3,2,3,4,5,4,3,2,1,6,7,8,9,7,5,4,2,2,1,2,4,6,7,54,3,8};
        System.out.println(heapSort(nums));
    }
}

快排

import java.util.Arrays;

public class Main {
    public static void quickSortHelper(int[] nums,int left,int right) {
        if(left>=right) {
            return;
        }
        int start = left;
        int end = right;
        int p=nums[left];
        while(left<right) {
            while(left<right&&nums[right]>p) {
                right--;
            }
            if(left<right) nums[left++] = nums[right]; // if防止超界
            while(left<right&&nums[left]<=p) {
                left++;
            }
            if(left<right) nums[right--] = nums[left];
        }
        nums[left] = p; //结束时left=right
        quickSortHelper(nums,start,left-1);
        quickSortHelper(nums,left+1,end);
    }
    public static void quickSort(int[] nums) {
        quickSortHelper(nums,0,nums.length-1);
    }
    public static void main(String[] args) {
        int[] nums = new int[]{3,2,4,5,3,1,6,7,8,9,8,7,5,6,7,4,2,1,6,5,4,3,2,1,8,9,0,1};
        quickSort(nums);
        System.out.println(Arrays.toString(nums));
    }
}

再看下单例

锁静态对象时所有实例运行到该处都会被锁住(类级别的锁),而锁实例对象的时候,该锁只对每个实例有效(实例级别的锁)。

单例模式、一致性哈希、redis防止数据丢失

redis内存淘汰策略

删除策略:定期删除、惰性删除(访问的时候,对过期时间进行检查,过期了就立刻删除,不返回值)。

内存淘汰策略:返回报错、LRU、根据过期时间LRU、随机删除、在设置了过期时间的键里随机删除、删除设置了过期时间的键里最近要过期的键,删除设置了过期时间的键里使用频率最少的键、删除所有键中使用频率最小的键。

协程

链表成环

推公式

FullGC怎么减少

Young GC,Eden区没有空间分配给新生对象,就会触发YoungGC。

Full GC,老年代空间不足,元空间不足,频繁Young GC,对象进入老年代。

排查代码。

dump查看或者visual vm之类查看对象状态。

如果时Full GC之后剩余对象不多,说明Eden区设置太小,导致生命周期比较短的对象进入了老年代。

如果时Full GC之后,old区回收率不大,可能:内存泄漏,大对象过多,老年代过小。

直接内存。

MySQL日志

redolog、undolog、binlog。

binlog用于主从复制中利用主库的binlog实现主从同步(逻辑日志)。

image-20220902174006134

relaylog:master主节点的binlog传到slave从节点后,被写到relay log里,从节点的slave sql线程从relaylog里读取日志然后应用到slave从节点本地。

不小心删除了一个表,怎么恢复

前提,针对每天有备份的数据和开启binlog日志的。

编辑binlog将其中的误操作命令删除,然后跑binlog。

redis为什么不考虑线程安全问题

服务端单线程。但是客户端还是要考虑线程安全的,这个可以使用原子指令。

redis大key删除

(一个key包含很多元素)

lazy free(redis提供异步延迟释放key内存的功能),放在background io子线程处理中,减少对主线程的阻塞,UNLINK命令是与DEL一样删除key功能的lazy free实现。

分区删除,例如每次删除500个。

触发一个脚本或者定时任务批量删除,

spring mvc执行流程

image-20220902175948936

DNS解析,域名和域名之间?

CDN

image-20220902180536842

域名解析

A记录:制定域名对应的IP地址。

CNAME记录:将一个域名映射到另一个域名。CDN节点域名,就是用CNAME进行映射的。

XSS攻击,什么语言,为什么,怎么解决,怎么预防

cross site scripting 跨站脚本攻击。将代码注入页面。通常javascript。

偏基础的八股

度小满 云平台研发面经