Java-spring相关八股

2023-11-20

1.Java中有哪几种方式来创建线程执行任务?

①.继承Thread类

public class zhouyuThread extends Thread{
    public static void main( string[] args){
        zhouyuThread thread = new ZhouyuThread( );
        thread.start();
}
    @override
    public void run() {
        system.out.println( "hello zhouyu" );
    }
}

重写run方法,通过.start()执行,但是占用了继承的名额,Java中的类是单继承的。、

②.实现Runnable接口

public class zhouyuThread implements Runnable{
    public static void main(string[ ] args){
        Thread thread = new Thread(new ZhouyuThread( ));
        thread.start();
}
    public void run() {
        system.out.println( "hello zhouyu" );
}

实现Runnable接口,实现run()方法,使用依然要用到Thread,这种方式更常用

③.实现Callable接口

public class zhouyuThread implements callable<string> {
    public static void main(string[] args) throws ExecutionException,InterruptedException{ 
        //可以打印线程结果
        FutureTask<string> futureTask = new FutureTask<>(new ZhouyuThread());
        Thread thread = new Thread(futureTask);
        thread.start();
        string result = futureTask.get();system.out.println(result);
    public string call() {
        return "hello zhouyu";
    }
}

④.利用线程池来创建线程

public class ZhouyuThread implements Runnable {
    public static void main(string[] args) throws ExecutionException,InterruptedException{
        Executorservice executorservice = Executors.newFixedThreadPool(10);
        executorservice.execute( new ZhouyuThread());
}
    public void run() {
    system.out.println( "hello zhouyu" );
}
}

实现Callable接口或者Runnable接口都可以,由ExecutorService来创建线程
总结:以上几种方式,底层都是基于Runnable

2.为什么不建议使用Executors来创建线程池?

创建的队列位LinkedBlockingQueue,是一个无界阻塞队列。如果使用该线程池执行任务,如果任务过多就会不断的添加到队列中,任务越多占用的内存就越多,最终会耗尽内存,导致OOM。

3.线程池有哪几种状态?每种状态分别表示什么?  有5种状态

        ①.RUNNING:表示线程池正常运行,既能接受新任务,也会正常处理队列中的任务

        ②. SHUTDOWN:当调用线程池的shutdown()方法时,线程池就进入SHUTDOWN状态,表示线程池处于正在关闭状态,此状态下线程池不会接受新任务,但是会继续把队列中的任务处理

        ③.STOP:当调用线程池的shutdownnow()方法时,线程池就进入STOP状态,表示线程池处于正在停止状态,此状态下线程池既不会接受新任务了,也不会处理队列中的任务,并且正在运行的线程也会被中断

        ④.TIDYING:线程池中没有线程在运行后,线程池的状态就会自动变为TIDYING,并且会调用terminated(),该方法是空方法,留给程序员进行扩展。

        ⑤.TERMINATED:terminated()方法执行完之后,线程池状态就会变为TERMINATED

4.Sychronized和ReentrantLock有哪些不同点?

锁信息表示是哪个线程加的锁 

5.ThreadLocal有哪些应用场景?它底层是如何实现的?

ThreadLocal是Java中所提供的线程本地存储机制,可以利用该机制将数据缓存在某个线程内部,该线程可以在任意时刻、任意方法中获取缓存的数据[set()、get()]

ThreadLocal底层是通过ThreadLocalMap来实现的,每个Thread对象(注意不是ThreadLocal对象)中都存在一个ThreadLocalMap, Map的key为ThreadLocal对象,Map的value为需要缓存的值。

如果在线程池中使用ThreadLocal会造成内存泄漏,当ThreadLocal对象使用完之后,应该要把设置的key,value,也就是Entry对象进行回收:手动调用ThreadLocal的remove方法,手动清楚Entry对象。

ThreadLocal经典的应用场景就是连接管理一个线程持有一个连接,该连接对象可以在不同的方法之间进行传递,线程之间不共享同一个连接)

6.ReentrantLock分为公平锁和非公平锁,那底层分别是如何实现的?

首先不管是公平锁和非公平锁,它们的底层实现都会使用AQS来进行排队,它们的区别在于线程在使用lock()方法加锁时:

1.如果是公平锁,会先检查AQS队列中是否存在线程在排队,如果有线程在排队,则当前线程也进行排队
2.如果是非公平锁,则不会去检查是否有线程在排队,而是直接竞争锁。(强不强的到另说)

另外,不管是公平锁还是非公平锁,一旦没竞争到锁,都会进行排队,当锁释放时,都是唤醒排在最前面的线程,所以非公平锁只是体现在了线程加锁阶段,而没有体现在线程被唤醒阶段,ReentrantLock是可重入锁(获得锁的线程可再次加锁),不管是公平锁还是非公平锁都是可重入的。

7.Tomcat中为什么要使用自定义类加载器?

一个Tomcat中可以部署多个应用,而每个应用中都存在很多类,并且各个应用中的类是独立的,全类名是可以相同的,比如一个订单系统中可能存在com.zhouyu.Usea类,一个库存系统中可能也存在comzhouyu.User类,一个Tomcat,不管内部部署了多少应用,Tomcat启动之后就是一个Java进程,也就是一个NVM,所以如果Tomcat中只存在一个类加载器,比如默AppClassLoader,那么就只能加载一个com.zhouyuUser类,这是有问题的,而在Tomcat中,会为部署的每个应用都生成一个类加载器实例,名字叫做WebAppClssLoader,这样Tomcat中每个应用就可以使用自己的类加载器去加载自己的类,从而达到应用之间的类隔离,不出现冲突。另外Tomcat还利用自定义加载器实现了热加载功能

说白了就是如果使用默认的类加载器,只能加载一个,而使用了自定义的类加载器,则可以加载多个。

8.JDK、JRE、JVM之间的区别

JDK(Java SE Development Kit)是java标准开发包,它提供了编译、运行Java程序所需的各种工具和资源,包括Java编译器Java运行时环境,以及常用的Java类库

JRE(Java Runtime Environment),Java运行环境,用于运行Java的字节码文件。普通用户而只需要安装JRE来运行Java程序,而程序开发者必须安装JDK来编译、调试程序。
JVM(Java Virtual Machine),Java虚拟机,是JRE的一部分,它是整个java实现跨平台的最核心的部分,负责运行字节码文件。

JDK中包含了JRE,JRE中包含了JVM。另外,JVM在执行Java字节码时,需要把字节码解释为机器指令,而不同操作系统的机器指令是有可能不一样的,所以就导致不同操作系统上的JVM是不一样的,所以我们在安装JDK时需要选择操作系统。

9.hashCode()与equals()之间的关系

在Java的一些集合类的实现中,在比较两个对象是否相等时,会先调用对象的hashCode()方法得到hashCode(相当于对象的指纹)进行比较,如果hashCode不相同,就可以直接认为这两个对象不相同,如果hashCode相同,那么就会进一步调用equals()方法进行比较。而equals()方法,就是用来最终确定两个对象是不是相等的,通常equals方法的实现会比较重,逻辑比较多,而hashCode()主要就是得到一个哈希值,实际上就一个数字,相对而言比较轻,所以在比较两个对象时,通常都会先根据hashCode先比较一下。
如果我们重写了equals()方法,那么就要注意hashCode()方法,一定要保证能遵守上述规则。

 比如说这里,重写的hashCode()直接比较name属性值就通过初级判断。

10. String、StringBuffer、StringBuilder的区别
        1.String是不可变的(常量,一旦创建就不能被修改),如果尝试去修改,会新生成一个字符串对象,StringBuffer和StringBuilder是可变的

        2. StringBuffer是线程安全的(有sychronized关键字,上锁了),StringBuilder是线程不安全的,所以在单线程环境下StringBuilder效率会更高

 此时s是“ab",但是是新生成的字符串对象

11.泛型中extends和super的区别
        1.<? extends T>表示包括T在内的任何T的子类

        2. <? super T>表示包括T在内的任何T的父类

这里因为指定了泛型的范围,所以添加字符串就不行,同时注意到创建对象的类可以不写泛型

泛型只是在编译时进行的 

12. ==和equals方法的区别
.        ==:如果是基本数据类型(8种),比较是值,如果是引用类型,比较的是引用地址
·        equals:具体看各个类重写equals方法之后的比较逻辑,比如String类,虽然是引用类型,但是string类中重写了equals方法,方法内部比较的是字符串中的各个字符是否全部相等。

13.重载和重写的区别

        ·重载(Overload):在一个类中,同名的方法如果有不同的参数列表(比如参数类型不同、参数个数不同,参数顺序不同)则视为重载。
        ·重写(Override):从字面上看,重写就是重新写一遍的意思。其实就是在子类中把父类本身有的方法重新写一遍。子类继承了父类的方法,但有时子类并不想原封不动的继承父类中的某个方法,所以在方法名,参数列表,返回类型都相同(子类中方法的返回值可以是父类中方法返回值的子类)的情况下,对方法体进行修改,这就是重写。但要注意子类方法的访问修饰权限(public那些)不能小于父类的。

14.List和Set的区别

        ·List:有序,按对象插入的顺序保存对象,可重复,允许多个Null元素对象,可以使用lterator取出所有元素,在逐一遍历,还可以使用get(int index)获取指定下标的元素
        .Set:无序,不可重复,最多允许有一个Null元素对象,取元素时只能用lterator接口取得所有元素,在逐一遍历各个元素

15. Jdk1.7到Jdk1.8 HashMap发生了什么变化(底层)?
        1.1.7中底层是数组+链表,1.8中底层是数组+链表+红黑树,加红黑树的目的是提高HashMap插入和查询整体效率

        2.1.7中链表插入使用的是头插法,1.8中链表插入使用的是尾插法,因为1.8中插入key和value时需要判断链表元素个数,所以需要遍历链表统计链表元素个数(存在就更新),所以正好就直接使用尾插法
        3.1.7中哈希算法比较复杂,存在各种右移与异或运算,1.8中进行了简化,因为复杂的哈希算法的目的就是提高散列性,来提供HashMap的整体效率,而1.8中新增了红黑树,所以可以适当的简化哈希算法,节省CPU资源

16.HashMap的Put方法

        1.根据Key通过哈希算法与与运算得出数组下标
        2.如果数组下标位置元素为空,则将key和value封装为Entry对象(JDK1.7中是Entry对象,JDK1.8中是Node对象))并放入该位置
        3.如果数组下标位置元素不为空,则要分情况讨论
                a.如果是JDK1.7,则先判断是否需要扩容,如果要扩容就进行扩容,如果不用扩容就生成Entry对象,并使用头插法添加到当前位置的链表中(先判断需不需要扩容,然后头插法插entry对象
                b.如果是JDK1.8,则会先判断当前位置上的Node的类型,看是红黑树Node,还是链表Node
                        i.如果是红黑树Node,则将key和value封装为一个红黑树节点并添加到红黑树中去,在这个过程中会判断红黑树中是否存在当前key,如果存在则更新value
                        ii.如果此位置上的Node对象是链表节点,则将key和value封装为一个链表Node并通过尾插法插入到链表的最后位置去,因为是尾插法,所以需要遍历链表,在遍历链表的过程中会判断是否存在当前key,如果存在则更新value,当遍历完链表后,将新链表Node插入到链表中,插入到链表后,会看当前链表的节点个数,如果大于等于8,那么则会将该链表转成红黑树
                        ii.将key和value封装为Node插入到链表或红黑树中后,再判断是否需要进行扩容,如果需要就扩容,如果不需要就结束PUT方法  (先判断存在否,不存在尾插法插入,存在更新,然后再判断需不需要扩容

17.深拷贝和浅拷贝
        深拷贝和浅拷贝就是指对象的拷贝,一个对象中存在两种类型的属性,一种是基本数据类型,一种是实例对象的引用(对象的属性可能包含其他对象[类中包含了其他类对象])。

        1.浅拷贝是指,只会拷贝基本数据类型的值,以及实例对象的引用地址,并不会复制一份引用地址所指向的对象,也就是浅拷贝出来的对象,内部的类属性指向的是同一个对象
        2.深拷贝是指,既会拷贝基本数据类型的值,也会针对实例对象的引用地址所指向的对象进行复制,深拷贝出来的对象,内部的属性指向的不是同一个对象

18.HashMap的扩容机制原理

        1.7版本
        1.先生成新数组
        2.遍历老数组中的每个位置上的链表上的每个元素
        3.取每个元素的key,并基于新数组长度,计算出每个元素在新数组中的下标

        4.将元素添加到新数组中去
        5.所有元素转移完了之后,将新数组赋值给HashMap对象的table属性
        1.8版本
        1.先生成新数组(通常都是原数组大小的两倍)
        2.遍历老数组中的每个位置上的链表或红黑树
        3.如果是链表,则直接将链表中的每个元素重新计算下标,并添加到新数组中去
        4.如果是红黑树,则先遍历红黑树,先计算出红黑树中每个元素对应在新数组中的下标位置
                a.统计每个下标位置的元素个数
                b.如果该位置下的元素个数超过了8,则生成一个新的红黑树,并将根节点的添加到新数组的对应位置

                c.如果该位置下的元素个数没有超过8,那么则生成一个链表,并将链表的头节点添加到新数组的对应位置

        5.所有元素转移完了之后,将新数组赋值给HashMap对象的table属性

19.CopyOnWriteArrayList的底层原理是怎样的
        1.首先CopyOnWriteArraylist内部也是用过数组来实现的,在向CopyOnWriteArrayList添加元素时,会复制一个新的数组(长度+1),写操作在新数组上进行读操作在原数组上进行
        2.并且,写操作会加锁(ReentantLock),防止出现并发写入丢失数据的问题
        3.写操作结束之后会把原数组指向指向新数组
        4.CopyOnWriteArraylist允许在写操作时来读取数据,大大提高了读的性能,因此适合读多写少的应用场景,但是CopyOnWriteArrayList会比较占内存(复制出了一个新数组),同时可能读到的数据不是实时最新的数据,所以不适合实时性要求很高的场景

        

 写操作时加了锁,同时注意到复制数组的时候长度+1

20.什么是字节码?采用字节码的好处是什么?
        编译器(javac)将Java源文件(*.java)文件编译成为字节码文件(*.class),可以做到一次编译到处运行,windows上编译好的class文件,可以直接在linux上运行,通过这种方式做到跨平台,不过Java的跨平台有一个前提条件,就是不同的操作系统上安装的IDK或JRE是不一样的,虽然字节码是通用的,但是需要把字节码解释成各个操作系统的机器码是需要不同的解释器的,所以针对各个操作系统需要有各自的JDK或JRE。
        采用字节码的好处,一方面实现了跨平台,另外一方面也提高了代码执行的性能编译器在编译源代码时可以做一些编译期的优化,比如锁消除、标量替换、方法内联等。

21. Java中的异常体系是怎样的
        . Java中的所有异常都来自顶级父类Throwable

        .Throwable下有两个子类Exception和Error。
        ·Error表示非常严重的错误,比如java.lang.StackOverFlowError(栈溢出)和Java.lang.OutOfMemoryError(内存满),通常这些错误出现时,仅仅想靠程序自己是解决不了的,可能是虚拟机、磁盘、操作系统层面出现的问题了,所以通常也不建议在代码中去捕获这些Error,因为捕获的意义不大,因为程序可能已经根本运行不了了。
        ·Exception表示异常,表示程序出现Exception时,是可以靠程序自己来解决的,比如NullPointerException、lllegalAccessException等,我们可以捕获这些异常来做特殊处理。
        . Exception的子类通常又可以分为RuntimeException和非RuntimeException两类
        .RunTimeException表示运行期异常,表示这个异常是在代码运行过程中抛出的,这些异常是非检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的(比如说除数为0),程序应该从逻辑角度尽可能避免这类异常的发生,如NullPointerException、IndexOutOfBoundsException(下标越界)等。
        ·非RuntimeException表示非运行期异常(变异时就要解决),也就是我们常说的检查异常,是必须进行处理的异常,如果不处理,程序就不能检查异常通过。如IOException(程序编译时认为无法进行IO)、SQLException等以及用户自定义的Exception异常

22.Java中有哪些类加载器
        JDK自带有三个类加载器: bootstrap ClassLoaderExtClassLoaderAppClassLoader
        ·BootstrapClassLoader是ExtClassLoader的父类加载器,默认负责加载%JAVA_HOME%lib下的jar包和class文件。

        .ExtClassLoader是AppClassLoader的父类加载器,负责加载%JAVA_HOME%/lib/ext文件夹下的jar包和class类。

        . AppClassLoader是自定义类加载器的父类,负责加载classpath下的类文件。

23.说说类加载器双亲委派模型

        JVM中存在三个默认的类加载器:
        1.BootstrapClassLoader
        2. ExtClassLoader
        3. AppClassLoader
        AppClassLoader的父加载器是ExtClassLoader,ExtClassLoader的父加载器是BootstrapClassLoader。
        JVM在加载一个类时,会调用AppClassLoader的loadClass方法来加载这个类,不过在这个方法中会先使用ExtClassLoader的loadClass方法来加载类,同样ExtClassLoader的loadClass方法中会先使用BootstrapClassLoader来加载类,如果BootstrapClassLoader加载到了就直接成功,如果BootstrapClassLoader没有加载到,那么ExtClassLoader就会自己尝试加载该类,如果没有加载到,那么则会由AppClassLoader来加载这个类。
        所以,双亲委派指得是,JVM在加载类时,会委派给Ext和Bootstrap进行加载,如果没加载到才由自己进行加载。

24.JVM中哪些是线程共享区
堆区和方法区是所有线程共享的,栈、本地方法栈、程序计数器是每个线程独有的

方法区:存类的信息  堆:存类的对象       类和类的对象可能多个线程都要用,是共享的

虚拟机栈:存当前线程运行时的方法 

25.一个对象从加载到JVM,再到被GC清除,都经历了什么过程?

        1.首先把字节码文件内容加载到方法区
        2.   然后再根据类信息在堆区创建对象
        3.对象首先会分配在堆区中年轻代的Eden区,经过一次Minor GC后,对象如果存活,就会进入Suvivor区。在后续的每次Minor GC中,如果对象一直存活,就会在Suvivor区来回拷贝,每移动一次,年龄加1
        4.当年龄超过15后,对象依然存活,对象就会进入老年代
        5.   如果经过Full GC,被标记为垃圾对象,那么就会被GC线程清理掉

26. 怎么确定一个对象到底是不是垃圾?
        1.引用计数算法:这种方式是给堆内存当中的每个对象记录一个引用个数。引用个数为O的就认为是垃圾。这是早期JDK中使用的方式。引用计数无法解决循环引用的问题(对象互相引用)。
        2.可达性算法:这种方式是在内存中,从根对象向下一直找引用,找到的对象就不是垃圾,没找到的对象就是垃圾。

27.JVM有哪些垃圾回收算法?
        1.标记清除算法:
                a.标记阶段:把垃圾内存标记出来

                b.清除阶段:直接将垃圾内存回收。
                c.这种算法是比较简单的,但是有个很严重的问题,就是会产生大量的内存碎片
        2.复制算法:为了解决标记清除算法的内存碎片问题,就产生了复制算法。复制算法将内存分为大小相等的两半,每次只使用其中一半。垃圾回收时,将当前这一块的存活对象全部拷贝到另一半,然后当前这一半内存就可以直接清除。这种算法没有内存碎片,但是他的问题就在于浪费空间。而且,他的效率跟存活对象的个数有关。
        3.标记压缩算法:为了解决复制算法的缺陷,就提出了标记压缩算法。这种算法在标记阶段跟标记清除算法是一样的,但是在完成标记之后,不是直接清理垃圾内存,而是将存活对象往一端移动,然后将边界以外(存活对象的边界)的所有内存直接清除。

28.什么是STW?
        STW: Stop-The-World,是在垃圾回收算法执行过程当中,需要将JVM内存冻结的一种状态。在STW状态下,JAVA的所有线程都是停止执行的-GC线程除外,native方法可以执行,但是,不能与JVM交互。GC各种算法优化的重点,就是减少STW,同时这也是JVM调优的重点。

29.Java死锁如何避免?造成死锁的几个原因:
        1.一个资源每次只能被一个线程使用
        2.一个线程在阻塞等待某个资源时,不释放已占有资源
        3.一个线程已经获得的资源,在未使用完之前,不能被强行剥夺
        4.若干线程形成头尾相接的循环等待资源关系
        这是造成死锁必须要达到的4个条件,如果要避免死锁,只需要不满足其中某一个条件即可。而其中前3个条件是作为锁要符合的条件,所以要避免死锁就需要打破第4个条件,不出现循环等待锁的关系
        在开发过程中:
        1.要注意加锁顺序,保证每个线程按同样的顺序进行加锁(比如说都先加a锁,再加b锁)
        2.要注意加锁时限,可以针对所设置一个超时时间(超过一段时间释放持有的锁)
        3.要注意死锁检查,这是一种预防机制,确保在第一时间发现死锁并进行解决

30.线程池的底层工作原理
        线程池内部是通过队列+线程实现的,当我们利用线程池执行任务时:
        1.如果此时线程池中的线程数量小于corePoolSize(核心线程数),即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
        2.如果此时线程池中的线程数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。

        3.如果此时线程池中的线程数量大于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize(最大线程数),建新的线程来处理被添加的任务。
        4.如果此时线程池中的线程数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过handler所指定的策略来处理此任务。(拒绝策略拒绝此任务
        5.当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数

31.线程池为什么是先添加列队而不是先创建最大线程?
        当线程池中的核心线程都在忙时,如果继续往线程池中添加任务,那么任务会先放入队列,队列满了之后,才会新开线程(相当于上面的第三条)是为了避免创建过多的线程,从而降低系统的负载

32.ReentrantLock中tryLock()和lock()方法的区别
        1. tryLock)表示
尝试加锁,可能加到,也可能加不到,该方法不会阻塞线程,如果加到锁则返回true,没有加到则返回false
        2. lock()表示阻塞加锁,
线程会阻塞直到加到锁,方法也没有返回值

33.Sychronized的偏向锁、轻量级锁、重量级锁
        1.偏向锁(偏向某个线程):在锁对象的对象头中记录一下当前获取到该锁的线程ID,该线程下次如果又来获取该锁就可以直接获取到了

        2.轻量级锁:由偏向锁升级而来,当一个线程获取到锁后,此时这把锁是偏向锁,此时如果有第二个线程来竞争锁,偏向锁就会升级为轻量级锁,之所以叫轻量级锁,是为了和重量级锁区分开来,轻量级锁底层是通过自旋来实现的并不会阻塞线程
        3.如果自旋次数过多仍然没有获取到锁,则会升级为重量级锁,重量级锁会导致线程阻塞
        4.自旋锁:自旋锁就是线程在获取锁的过程中,不会去阻塞线程,也就无所谓唤醒线程,阻塞和唤醒这两个步骤都是需要操作系统去进行的,比较消耗时间,自旋锁是线程通过CAs获取预期的一个标记,如果没有获取到,则继续循环获取,如果获取到了则表示获取到了锁,这个过程线程一直在运行中,相对而言没有使用太多的操作系统资源,比较轻量。
 

34.谈谈你对AQS(AbstractQuenedSynchronizer,抽象的队列式同步器)的理解,AQS如何实现可重入锁?

        思想:如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中
        1.AQS是一个JAVA线程同步的框架。是JDK中很多锁工具的核心实现框架。
        2.在AQS中,维护了
一个信号量state和一个线程组成的双向链表队列。其中,这个线程队列,就是用来给线程排队的,而state就像是一个红绿灯,用来控制线程排队(阻塞)或者放行的。在不同的场景下,有不用的意义。
        3.在可重入锁这个场景下,state就用来表示加锁的次数。0标识无锁,每加一次锁,state就加1。释放锁state就减1。

        AQS是自旋锁(循环等待获取)

35. IOC的理解

        IOC表示控制反转。控制:1.控制对象的创建 2.控制对象内属性的赋值
本来这些需要程序员去创建的,但是现在用Spring,这两件事就交给Spring来做,我们要做的仅仅是定义类,以及定义哪些属性需要Spring来赋值(比如某个属性上加@Autowired)

        反转:表示一种对象控制权的反转

        好处:代码复用,减轻程序员的负担

36.Bean就是一个对象,凡是子类及带有方法或属性的类都要加上注册Bean到Spring IoC的注解(像是得实例化);(@Component , @Repository , @ Controller , @Service , @Configration

        把Bean理解为类的代理或代言人(实际上确实是通过反射、代理来实现的),这样它就能代表类拥有该拥有的东西了

37.注解

        注解:Annotation,就是一个标签,同时可以作为AOP的一个切入点

        注解分为两类:

        1、一类是使用Bean,即是把已经在xml文件中配置好的Bean拿来用,完成属性、方法的组装;比如@Autowired , @Resource,可以通过byTYPE(@Autowired)、byNAME(@Resource)的方式获取Bean;

        2、一类是注册Bean,@Component , @Repository , @ Controller , @Service , @Configration这些注解都是把你要实例化的对象转化成一个Bean,放在IoC容器中,等你要用的时候,它会和上面的@Autowired , @Resource配合到一起,把对象、属性、方法完美组装。

38. Spring事务传播机制
        多个事务方法相互调用时,事务如何在这些方法间传播,方法A是一个事务的方法,方法A执行过程中调用了方法B,那么方法B有无事务以及方法B对事务的要求不同都会对方法A的事务具体执行造成影响,同时方法A的事务对方法B的事务执行(假如方法B中加了@Transactional)也有影响,这种影响具体是什么就由两个方法所定义的事务传播类型所决定。
        1.REQUIRED(Spring默认的事务传播类型):如果当前没有事务,则自己新建一个事务,如果当前存在事务,则加入这个事务

39.Spring中的Bean创建的生命周期有哪些步骤

        Spring中一个Bean的创建大概分为以下几个步骤:
        1.推断构造方法(选择类当中的哪个构造方法)
        2.实例化
        3.填充属性,也就是依赖注入
        4.处理Aware回调
        5.初始化前,处理@PostConstruct注解

        6.初始化,处理lnitializingBean接口

        7.初始化后,进行AOP

40.Spring中的事务是如何实现的
        1. Spring事务底层是基于数据库事务和AOP机制的
        2.首先对于使用了@Transactional注解的Bean,Spring会创建一个代理对象作为Bean(如果不是代理对象的话,事务是会失效的)

        3.当调用代理对象的方法时,会先判断该方法上是否加了@Transactional注解
        4.如果加了,那么则利用事务管理器创建一个数据库连接
        5.并且修改数据库连接的autocommit属性为false,禁止此连接的自动提交,这是实现Spring事务非常重要的一步

        6.然后执行当前方法,方法中会执行sql
        7.执行完当前方法后,如果没有出现异常就直接提交事务
        8.如果出现了异常,并且这个异常是需要回滚的就会回滚事务,否则仍然提交事务

        9. Spring事务的隔离级别对应的就是数据库的隔离级别
        10. Spring事务的传播机制是Spring事务自己实现的,也是Spring事务中最复杂的
        11. Spring事务的传播机制是基于数据库连接来做的,一个数据库连接一个事务,如果传播机制配置为需要新开一个事务,那么实际上就是先建立一个新的数据库连接,在此新数据库连接上执行sql


41.Spring容器启动流程是怎样的

        在创建Spring容器,也就是启动Spring时:
        1.首先会进行扫描,扫描得到所有的BeanDefinition对象,并存在一个Map中
        2.然后筛选出非懒加载的单例BeanDefinition进行创建Bean,对于多例Bean不需要在启动过程中去进行创建,对于多例Bean会在每次获取Bean时利用BeanDefinition去创建,懒加载的单例Bean也是一样
        3.利用BeanDefinition创建Bean,就是创建Bean的生命周期,这期间包括了合并BeanDefinition、推断构造方法、实例化、属性填充、初始化前、初始化、初始化后等步骤,其中AOP就是发生在初始化后这一步骤中
        4.单例Bean创建完了之后,Spring会发布一个容器启动事件
        5. Spring启动结束
        6.在源码中会更复杂,比如源码中会提供一些模板方法,让子类来实现,比如源码中还涉及到一些BeanFactoryPostProcessor和BeanPostProcessor的注册,Spring的扫描就是通过BenaFactoryPostProcessor来实现的,依赖注入就是通过BeanPostProcessor来实现的
        7.在Spring启动过程中还会去处理@Import等注解

42.ApplicationContext和BeanFactory有什么区别
        BeanFactory是Spring中非常核心的组件,表示Bean工厂,可以生成Bean,维护Bean,而ApplicationContext继承了BeanFactory,所以ApplicationContext拥有BeanFactory所有的特点,也是一个Bean工厂,但是ApplicationContext除开继承了BeanFactory之外,还继承了诸如EnvironmentCapable、MessageSource、ApplicationEventPublisher等接口,从而ApplicationContext还有获取系统环境变量、国际化、事件发布等功能,这是BeanFactory所不具备的



43. @Bean注解:

        用来定义Bean,类似于XML中的<bean>标签,Spring在启动时,会对加了@Bean注解的方法进行解析,将方法的名字做为beanName,并通过执行方法得到bean对象

44.单例Bean和单例模式
        单例模式表示JVM中某个类的对象只会存在唯一—个。
        而单例Bean并不表示JVM中只能存在唯一的某个类的Bean对象。

45.Mybatis的优缺点
        优点:
        1,基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
        2.与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接;
        3.很好的与各种数据库兼容因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。
        4.能够与Spring很好的集成;
        5.提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
        缺点:
        1.SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。

        2.SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库

46.#{}和${}的区别是什么?
        #是预编译处理、是占位符,$是字符串替换、是拼接符
        Mybatis在处理#{}时,会将sql中的#替换为?号,调用PreparedStatement来赋值;
        Mybatis在处理${}时,会将sql中的$替换成变量的值,调用Statement来赋值;
        使用#{}可以有效的防止SQL注入(目前web应用网络攻击中最常见的手段之一),提高系统安全性。

47.Redis的过期键的删除策略
        Redis是key -value数据库,我们可以设置Redis中缓存的key的过期时间。Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理。
        ·惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
        ·定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
        Redis中同时使用了惰性过期和定期过期两种过期策略。
48.简述Redis事务实现

        Redis没有回滚机制,但是保证了语义,Redis是单线程的,事务只会被这个线程运行(能保证一致性)。

        1、事务开始
        MULT命令的执行,标识着一个事务的开始。MULT命令会将客户端状态的 flags属性中打开REDTS_MULTI 标识来完成的。(只做这一件事)
        2、命令入队(只是把相关命令放入队列当中)
        当一个客户端切换到事务状态之后,服务器会根据这个客户端发送来的命令来执行不同的操作。如果客户端发送的命令为MULTI、EXEC、WATCH、DISCARD中的一个,立即执行这个命令,否则将命令放入一个事务队列里面,然后向客户端返回QUEUED回复
        ·如果客户端发送的命令为EXEC、DISCARD、WATCH、MULTI四个命令的其中一个,那么服务器立即执行这个命令。
        ·如果客户端发送的是四个命令以外的其他命令,那么服务器并不立即执行这个命令。
首先检查此命令的格式是否正确(检查Redis的语法是否正确),如果不正确,服务器会在客户端状态(redisClient)的flags属性关闭REDIS_MULTI标识,并且返回错误信息给客户端。
        如果正确,将这个命令放入一个事务队列里面,然后向客户端返回QUEUED回复
        事务队列是按照FIFO的方式保存入队的命令

        3、事务执行
        客户端发送EXEC命令,服务器执行EXEC命令逻辑。
        ·如果客户端状态的flags 属性不包含REDIS_MULTI标识,或者包含REDIS_DIRTY_CAS或者REDIS_DIRTY_EXEC标识,那么就直接取消事务的执行。
        ·否则客户端处于事务状态 (flags有 REDIS_MULTI标识),服务器会遍历客户端的事务队列,然后执行事务队列中的所有命令,最后将返回结果全部返回给客户端;
        redis不支持事务回滚机制,但是它会检查每一个事务中的命令是否错误(只是检查语法)。
        Redis事务不支持检查那些程序员自己逻辑错误。例如对 String类型的数据库键执行对HashMap类型的操作!
        ·WATCH命令是一个乐观锁,可以为Redis事务提供check-and-set (CAS)行为。可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令
        ·MULTI命令用于开启一个事务,它总是返回OK。MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。
        ·EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。当操作被打断时,返回空值null
        ·通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务,并且客户端会从事务状态中退出。

        . UNWATCH命令可以取消watch对所有key的监控。

49.Redis集群策略

        Redis提供了三种集群策略:
        1.主从模式:这种模式比较简单,主库可以读写,并且会和从库进行数据同步(异步方式),这种模式下,客户端直接连主库或某个从库,但是当主库或从库宕机后,客户端需要手动修改IP,另外,这种模式也比较难进行扩容,整个集群所能存储的数据受到某台机器的内存容量(主库),所以不可能支持特大数据量
        2.哨兵模式:这种模式在主从的基础上新增了哨兵节点,但主库节点宕机后,哨兵会发现主库节点宕机,然后在从库中选择一个库作为进的主库,另外哨兵也可以做集群,从而可以保证但某一个哨兵节点宕机后,还有其他哨兵节点可以继续工作,这种模式可以比较好的保证Redis集群的高可用,但是仍然不能很好的解决Redis的容量上限问题。
        3.Cluster模式: Cluster模式是用得比较多的模式,它支持多主多从,这种模式会按照key进行槽位的分配(比如说根据hash值),可以使得不同的key分散到不同的主节点上,利用这种模式可以使得整个集群支持更大的数据容量(可以增加主库的个数),同时每个主节点可以拥有自己的多个从节点,如果该主节点宕机,会从它的从节点中选举一个新的主节点
        对于这三种模式,如果Redis要存的数据量不大,可以选择哨兵模式,如果Redis要存的数据量大,并且需要持续的扩容,那么选择Cluster模式。

50.Redis单线程为什么这么快

        Redis基于Reactor模式开发了网络事件处理器、文件事件处理器fileeventhandler,它是单线程的,所以Redis才叫做单线程的模型,它采用IO多路复用机制来同时监听多个Socket,根据Socket上的事件类型来选择对应的事件处理器(不同的命令有不同的处理器,命令请求处理器、命令回复处理器、连接应答处理器等)来处理这个事件。可以实现高性能的网络通信模型,又可以跟内部其他单线程的模块进行对接,保证了Redis内部的线程模型的简单性。

        监听多个Socket,放入一个队列中排队,每次从队列中有序、同步取出一个Socket给事件处理器(要先经过事件分派器的分派)

        单线程快的原因:1.纯内存操作
        2.核心是基于非阻塞的IO多路复用机制
        3.单线程反而避免了多线程的频繁上下文切换带来的性能问题(java是多线程的,如果再并发且单核的情况下,cpu切换非常损耗性能)

51.什么是CAP理论
        CAP理论是分布式领域中非常重要的一个指导理论,C(Consistency)表示强一致性,A(Availability)表示可用性,P(Partition Tolerance)表示分区容错性,CAP理论指出在目前的硬件条件下,一个分布式系统是必须要保证分区容错性的,而在这个前提下,分布式系统要么保证CP,要么保证AP,无法同时保证CAP

        强一致性表示,一个分布式系统中各个结点之间能及时的同步数据,在数据同步过程中,是不能对外提供服务的,不然就会造成数据不—致,所以强一致性和可用性是不能同时满足的。

        可用性表示,一个分布式系统对外要保证可用。即服务一直可用,而且是正常响应时间。
        分区容错性表示,一个系统虽然是分布式的,但是对外看上去应该是一个整体,不能由于分布式系统内部的某个结点挂掉,或网络出现了故障,而导致系统对外出现异常。比如现在的分布式系统中有某一个或者几个机器宕掉了,其他剩下的机器还能够正常运转满足系统需求,对于用户而言并没有什么体验上的影响。

52.什么是BASE理论

        BASE是Basically Available (基本可用) 、Soft state(软状态)和Eventually consistent(最终一致性),BASE理论是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结,是基于CAP定理逐步演化而来的。BASE理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。
        基本可用:
        ·响应时间上的损失:正常情况下,处理用户请求需要0.5s返回结果,但是由于系统出现故障,处理用户请求的时间变为3s。
        系统功能上的损失:正常情况下,用户可以使用系统的全部功能,但是由于系统访问量突然剧增,系统的部分非核心功能无法使用。
        软状态:数据同步允许一定的延迟
        最终一致性:系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态,不要求实时

53.什么是ZAB协议
        ZAB协议是Zookeeper用来实现一致性的原子广播协议,该协议描述了Zookeeper是如何实现一致性的,分为三个阶段:

        1,领导者选举阶段:从Zookeeper集群中选出一个节点作为Leader,所有的写请求都会由Leader节点来处理(读的话其他节点进行处理)
        2.数据同步阶段:集群中所有节点中的数据要和Leader节点保持一致,如果不一致则要进行同步
        3.请求广播阶段:当Leader节点接收到写请求时,会利用两阶段提交来广播该写请求,使得写请求像事务一样在其他节点上执行,达到节点上的数据实时一致
但值得注意的是,Zookeeper只是尽量的在达到强一致性,实际上仍然只是最终一致性的。

54.Zookeeper集群中节点之间数据是如何同步的(一次是写日志,一个是commit命令
        1.首先集群启动时,会先进行领导者选举,确定哪个节点是Leader,哪些节点是Follower和Observer

        2.然后Leader会和其他节点进行数据同步,采用发送快照和发送Diff日志的方式
        3.集群在工作过程中,所有的写请求都会交给Leader节点来进行处理从节点只能处理读请求4.Leader节点收到一个写请求时,会通过两阶段机制来处理
        5.Leader节点会将该写请求对应的日志发送给其他Follower节点,并等待Follower节点持久化日志成功

        6. Follower节点收到日志后会进行持久化,如果持久化成功则发送一个Ack给Leader节点
        7.当Leader节点收到半数以上的Ack后,就会开始提交,先更新Leader节点本地的内存数据
        8.然后发送commit命令给Follower节点,Follower节点收到commit命令后就会更新各自本地内存数据
        9.同时Leader节点还是将当前写请求直接发送给Observer节点,Observer节点收到Leader发过来的写请求后直接执行更新本地内存数据
        10.最后Leader节点返回客户端写请求响应成功
        11.通过同步机制和两阶段提交机制来达到集群中节点数据一致
54.负载均衡算法有哪些
        1、轮询法:将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务器实际的连接数和当前的系统负载。
        2、随机法:通过系统的随机算法,根据后端服务器的列表大小值来随机选取其中的一台服务器进行访问。由概率统计理论可以得知,随着客户端调用服务端的次数增多,其实际效果越来越接近于平均分配调用量到后端的每一台服务器,也就是轮询的结果。
        3、源地址哈希法:源地址哈希的思想是根据获取客户端的IP地址通过哈希函数计算得到的一个数值,用该数值对服务器列表的大小进行取模运算,得到的结果便是客服端要访问服务器的序号。采用源地址哈希法进行负载均衡,同一IP地址的客户端,当后端服务器列表不变时,它每次都会映射到同一台后端服务器进行访问。
        4、加权轮询法:不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。给配置高、负载低的机器配置更高的权重,让其处理更多的请求;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载,加权轮询能很好地处理这一问题,并将请求顺序且按照权重分配到后端。
        5、加权随机法:与加权轮询法一样,加权随机法也根据后端机器的配置,系统的负载分配不同的权重。不同的是,它是按照权重随机请求后端服务器,而非顺序。
        6、最小连接数法:最小连接数算法比较灵活和智能,由于后端服务器的配置不尽相同,对于请求的处理有快有慢,它是根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前的请求,尽可能地提高后端服务的利用效率,将负责合理地分流到每一台服务器。

55.Redis主从复制的核心原理(实现同步)
        Redis的主从复制是提高Redis的可靠性的有效措施,主从复制的流程如下:

        1.集群启动时,主从库间会先建立连接,为全量复制做准备
        2.主库将所有数据同步给从库。从库收到数据后,在本地完成数据加载,这个过程依赖于内存快照RDB
        3.在主库将数据同步给从库的过程中,主库不会阻塞,仍然可以正常接收请求。否则,redis的服务就被中断了。但是,这些请求中的写操作并没有记录到刚刚生成的RDB文件中。为了保证主从库的数据一致性主库会在内存中用专门的replicationbuffer,记录RDB文件生成收到的所有写操作
        4,最后,也就是第三个阶段,主库会把第二阶段执行过程中新收到的写命令,再发送给从库。具体的操作是,当主库完成RDB文件发送后,就会把此时replocation buffer中修改操作发送给从库,从库再执行这些操作。这样一来,主从库就实现同步了

        5.后续主库和从库都可以处理客户端读操作,写操作只能交给主库处理,主库接收到写操作后,还会将写操作发送给从库,实现增量同步。


57.分布式架构下,Session共享有什么方案
        1、采用无状态服务,抛弃session
        2、存入cookie(有安全风险)
        3、服务器之间进行Session同步,这样可以保证每个服务器上都有全部的Session信息,不过当服务器数量比较多的时候,同步是会有延迟甚至同步失败;
        4、IP绑定策略
        使用Nginx(或其他复杂均衡软硬件)中的IP绑定策略,同一个IP只能在指定的同一个机器访问,但是这样做失去了负载均衡的意义,当挂掉一台服务器的时候,会影响一批用户的使用,风险很大;
        5、使用Redis存储
        把Session放到Redis 中存储(利用Redis的主从复制),虽然架构上变得复杂,并且需要多访问一次Redis,但是这种方案带来的好处也是很大的:
        。实现了Session共享;
        。可以水平扩展(增加Redis 服务器);
        ·服务器重启Session不丢失(不过也要注意Session在Redis 中的刷新/失效机制);。

        不仅可以跨服务器Session共享,甚至可以跨平台(例如网页端和APP端)。
        

Session是保存在本地服务器当中的,同时会发送一个sessionID给客户端,客户端那边保存的是cookie。

58.如何实现接口的幂等性(多次访问的接口是同一个接口的情况)
        唯一id。每次操作,都根据操作和内容生成唯一的id,在执行之前先判断id是否存在,如果不存在则执行后续操作,并且保存到数据库或者redis等。
        ·服务端提供发送token的接口,业务调用接口前先获取token,然后调用业务接口请求时把token携带过去,服务器判断token是否存在redis中,不存在表示第一次请求,可以继续执行业务,执行业务完成后,最后需要把redis中的token删除

        ·建去重表。将业务中有唯一标识的字段保存到去重表,如果表中存在,则表示已经处理过了
        ·版本控制。增加版本号,当版本号符合时,才能更新数据(相当于乐观锁)
        ·状态控制。例如订单有状态已支付未支付支付中支付失败,当处于未支付的时候才允许修改为支付中等

59.简述zk的命名服务、配置管理、集群管理(ZK的三个作用)
        命名服务:
        通过指定的名字来获取资源或者服务地址Zookeeper可以创建一个全局唯一的路径,这个路径就可以作为一个名字。被命名的实体可以是集群中的机器,服务的地址,或者是远程的对象等。一些分布式服务框架(RPC、RMI)中的服务地址列表,通过使用命名服务,客户端应用能够根据特定的名字来获取资源的实体、服务地址和提供者信息
        配置管理:
        实际项目开发中,经常使用.properties或者xml需要配置很多信息,如数据库连接信息、fps地址端口等等。程序分布式部署时,如果把程序的这些配置信息保存在zk的znode节点下,当你要修改配置,即znode会发生变化时,可以通过改变zk中某个目录节点的内容,利用watcher通知给各个客户端(这里指其他节点),从而更改配置。
        集群管理:
        集群管理包括集群监控集群控制,就是监控集群机器状态,剔除机器和加入机器。 zookeeper可以方便集群机器的管理,它可以实时监控znode节点的变化,一旦发现有机器挂了,该机器就会与zk断开连接,对应的临时目录节点会被删除,其他所有机器都收到通知。新机器加入也是类似。
60. 讲下Zookeeper中的watch机制
        客户端,可以通过在znode上设置watch,实现实时监听znode的变化(就是向ZooKeeper服务器注册一个Watch监听)
        Watch事件是一个一次性的触发器,当被设置了Watch的数据发生了改变的时候,则服务器将这个改变发送给设置了Watch的客户端
        ·父节点的创建,修改,删除都会触发Watcher事件。

        ·子节点的创建,删除会触发Watcher事件。
        一次性:一旦被触发就会移除,再次使用需要重新注册,因为每次变动都需要通知所有客户端,一次性可以减轻压力,3.6.0默认持久递归可以触发多次
        轻量:只通知发生了事件,不会告知事件内容,减轻服务器和带宽压力(事件的具体内容需要客户端去服务端去取)。

61.分布式系统中常用的缓存方案有哪些
        ·客户端缓存:页面和浏览器缓存,APP缓存,H5缓存,localStorage和sessionStorage

        ·CDN缓存(请求链接经过DNS后再发给CDN):内容存储:数据的缓存,内容分发:负载均衡(如果是多个服务端的话,进行转发)
        . nginx缓存:静态资源
        ·服务端缓存:本地缓存(Map,List等再容器中),外部缓存(Redis)
        ·数据库缓存:持久层缓存(mybatis,hibernate多级缓存),mysq|查询缓存(Key是sql语言,Value是查询出来的result)

        操作系统缓存: PageCache、BufferCache
62.缓存过期都有哪些策略?
        ·定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量
        ·惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,但是很消耗内存、许多的过期数据都还存在内存中。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
        ·定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key(是随机的),并清除其中已过期的key。该策略是定时过期和惰性过期的折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
        ·分桶策略:定期过期的优化,将过期时间点相近的key放在一起,按时间扫描分桶。(比如说5点过期的放在一个桶中,6点过期的放在一个桶中,超过5点清除桶,超过6点清楚另外一个桶)

63.布隆过滤器原理,优缺点
        ·位图: int[10],每个int类型的整数是4*8=32个bit,则lint[10]一共有320 bit,每个bit非o即1,初始化时都是0
        ·添加数据时∶将数据进行hash得到hash值,对应到bit位,将该bit改为1,hash函数可以定义多个,则―一个数据添加会将多个(hash函数个数) bit改为1,多个hash函数的目的是减少hash碰撞的概率
        ·查询数据: hash函数计算得到hash值,对应到bit中,如果有一个为0,则说明数据不在bit中,如果都为1,则该数据可能在bit中(因为有冲突)
        优点:
        ·占用内存小
        ·增加和查询元素的时间复杂度为: O(K),(K为哈希函数的个数,一般比较小),与数据量大小无关哈希函数相互之间没有关系,方便硬件并行运算
        ·布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
        缺点:
        ·误判率,即存在假阳性(False Position),不能准确判断元素是否在集合中不能获取元素本身。一般情况下不能从布隆过滤器中删除元素

64.常见的缓存淘汰算法
        ·FlFO (First ln First Out,先进先出),根据缓存被存储的时间,离当前最远的数据优先被淘汰;

        ·LRU (LeastRecentlyUsed,最近最少使用),根据最近被使用的时间,离当前最远的数据优先被淘汰;

        ·LFU (LeastFrequentlyUsed,最不经常使用),在一段时间内,缓存数据被使用次数最少的会被淘汰。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java-spring相关八股 的相关文章

随机推荐

  • 导航电子地图的制作过程

    背景知识 1 导航原理 现代导航通过实时测定运动客体的当前位置及速度 方向等运动参数 以此为基础通过分析和计算 确定若干条符合某些条件要求如 距离 速度 时间 方向 的路线和行驶方案 然后利用系统进行引导和控制客体沿确定路线行驶 并提供必要
  • 软件测试基础——WEB测试模块

    软件测试工程师体系 web测试模块 web测试模块脑图 本文内容以脑图形式展示
  • 什么是接口测试,如何做接口测试?

    比起点点点的功能测试 接口测试 显得专业又高大上 也因此让有些初级测试人员 望而生畏 别担心 其实接口测试也是功能测试的一种 它是针对接口进行的功能测试 写在前面 本文参考了茹炳晟老师的 测试工程师 全栈技术进阶与实践 并结合自己的理解进行
  • Kafka 监控系统Eagle 使用教程 V1.4.0

    1 下载安装zookeeper 2 下载安装kafka 3 下载安装kafka eagle http download kafka eagle org tar zvxf kafka eagle bin 1 4 0 tar gz 4 配置JA
  • 命令注入漏洞(1)

    命令注入漏洞原理 其实命令注入漏洞也叫命令行注入漏洞 此漏洞是指web应用程序中调用了系统可执行命令的函数 而且输入的参数是可控的 如果黑客拼接了注入命令 就可以进行非法操作了 靶机搭建 链接 https pan baidu com s 1
  • 用栈实现括号匹配问题

    通过观察 我们可以发现 括号匹配的字符串 左括号与右括号数目一定相等 且遇到右括号时 必定有与之相匹配的括号在之前最近出现过 这样 可以整理解决问题的思路如下 假设有一串带括号的字符串 依次访问每一个字符 遇到左括号入栈 遇到右括号时 取栈
  • 有关bool(布尔)类型在C语言中的应用

    文章目录 前言 一 bool类型是什么 二 使用举例 总结 前言 由于学习过程中接触到了bool类型 产生了浓厚的兴趣 便写下这一篇文章来阐述bool类型的大概情况 一 bool类型是什么 bool 布尔 是在C99标准中引入的类型 是以英
  • GPT-4来了,但大模型的诸多未解之谜仍然未解

    导语 在3月14日 OpenAI 的 GPT 4 正式发布 它拥有多模态能力 可以接受图像输入并理解图像内容 可接受的文字输入长度增加到 3 2 万个 token 在多种专业和学术基准测试中取得好成绩 然而 功能强大的 GPT 4 与早期的
  • 关于串口调试助手上面的DTR和RTS

    开发调试过程中 突然XCOM串口调试助手无法接发数据 而用了sscom却可以实现正常功能 emo了很久 对比了两个软件对串口的设置 包括波特率 停止位 校验位等设置 也没发现异端 以为是sscom这个软件禁用了XCOM 后来仔细比对发现 X
  • DDR布线要求及拓扑结构分析

    在DDR的PCB设计中 一般需要考虑等长和拓扑结构 等长比较好处理 给出一定的等长精度通常是PCB设计师是能够完成的 但对于不同的速率的DDR 选择合适的拓扑结构非常关键 在DDR布线中经常使用的T型拓扑结构和菊花链拓扑结构 下面主要介绍这
  • 动手学数据分析 Task3

    动手学数据分析 Task3 一 concat merge join 二 groupby 一 concat merge join concat方法可以在两个维度上拼接 默认纵向凭借 axis 0 拼接方式默认外连接 pd concat obj
  • window全局对象的全局变量与脚本的全局变量间的关系

    如果脚本中的变量声明出现在命名元素之前 那这个变量的存在就会组织元素获取他的window属性 而如果脚本中的变量声明出现在命名元素之后 那么变量的显示赋值会覆盖该属性的隐式值
  • 数据库系列MySQL:优化配置文件

    配置流程 1 MySQL文件目录中后缀名为 ini文件的就是MySQL的默认配置文件 2 程序启动会先加载配置文件中的的配置 之后才会真正启动程序 3 更改完配置文件设置后需要重新启动服务端才可以生效 优化方案一 服务器内存 4 8GB k
  • 删除C++ std::string字符串中的空格

    介绍一个使用标准库算法删除std string字符串中空格的方法 代码如下 std string str1 Hello world str1 erase std remove if str1 begin str1 end unsigned
  • unity: C#的Action Event Delegate的异同

    目录 一 Action 二 Event 三 Action和Event区别 四 Delegate 总结 Action Event Delegate的异同 前言 Action Event和Delegate都是C 语言中的重要概念 分别用于管理函
  • Human3.6M数据集下载

    Download H36M annotations mkdir data cd data wget http visiondata cis upenn edu volumetric h36m h36m annot tar tar xf h3
  • 利用Apache Tika分页解析pdf文件内容

    Apache Tika 实现pdf文档分页提取内容 Apache Tika是一个多功能的文档内容提取工具 可以提取多种类型的文档内容 常用的如pdf office等格式 网上的例子基本上都是提取整篇文档内容 实际上用Tika提取pdf等文档
  • QT 实现点击窗口以外任何位置即关闭窗口

    bool QTipLabel eventFilter QObject o QEvent e switch e gt type ifdef Q DEAD CODE FROM QT4 MAC case QEvent KeyPress case
  • LeetCode-----第八题-----字符串转换整数 (atoi)

    字符串转换整数 atoi 难度 中等 请你来实现一个 atoi 函数 使其能将字符串转换成整数 首先 该函数会根据需要丢弃无用的开头空格字符 直到寻找到第一个非空格的字符为止 接下来的转化规则如下 如果第一个非空字符为正或者负号时 则将该符
  • Java-spring相关八股

    1 Java中有哪几种方式来创建线程执行任务 继承Thread类 public class zhouyuThread extends Thread public static void main string args zhouyuThre