面试题汇总

目录

第一部分:MySQL

1、MySQL的索引和数据结构能聊一下吗(数据结构,存储引擎,数据格式,B树IO;各种索引)

        MySQL对应相应的索引,索引的底层数据结构使用的是B+树或者是hash表生成的,不同类型的索引是跟存储引擎是相关的,如果用的是Innodb 或者是Myisam,那么底层的数据结构就是用的B+树实现的,如果用的是Memory存储引擎,那么底层数据结构就是用的hash表实现的。

        不同的引擎表示的是不同的数据在磁盘上的组织形式。

        索引里面存储的数据的格式,一般来说是根据某一个key来找对应的value,key索引的是某列的值,value对应的是一行数据,选择k-v格式数据的时候,可以用hash表,可以用二叉树、AVL树,红黑树,B树,B+树。但是用任何一种形式的二叉树,最终都会使树的高度变高。树变高会影响我们整体的IO的次数,从而影响到我们整体的访问速率。所以使用了B+树的数据结构,在某一个节点里面尽可能多的存储数据,让这棵树变低,减少IO的次数,提高数据的访问效率。

        同时在MySQL里面有主键索引,唯一索引,主键索引,组合索引等各种分类,而我们日常经常使用的是主键索引和组合索引,主键索引和组合索引的时候会存在一系列回表、索引覆盖,最左匹配,索引下推等一系列的细节点需要我们了解的。使用SQL语句的时候,我们可以通过索引的一些点来进行优化,提高我们对数据的访问形式。

注意的点:

        1、Innodb和Myisam的不同。MySQL默认存储引擎是Innodb。

        InnoDB支持事务,默认的锁时行锁。 MyISAM不支持事务,默认的锁时表锁。 读写性能:InnoDB增删改性能更优;MyISAM查询性能更优。(事务,锁的粒度,性能)

        2、MySQL官网说的是默认的是B树,但是实际用的是B+树。

        3、Memory存储引擎只存key不存value,value是行指针,指向数据文件,在进行数据读取。

        4、Memory存储引擎在内存里,断电内存数据清空,Innodb可以做到磁盘持久化。

2、B树和B+树的区别是什么

        B树所有节点都存储数据,B+树只在叶子结点存储数据,非叶子节点存储的是key值。计算一下,三层B树和B+树的数据量,假设每个数据是1k,key值是10字节的话,B树:16*16*16=4096;B+树1600*1600*16=4096w,千万级别了。100字节也能到40多w数据量。

为啥不用hash表?

        1、hash表需要有好的hash算法,如果算法不合适,hash碰撞很厉害的话,会导致退化成链表,效率低很多。

        2、hash表不支持范围查询,挨个查询效率低。

        3、hash表占用更多的内存空间。

二叉树,红黑树等,分值只有两个,IO次数会多,查询效率就会变慢。

        局部性原理、磁盘预读。时间和空间局部性:被读过的数据,下次更大概率会被读。磁盘预读是:如果取了一个字符a,实际回取一个datapage页,是4k的幂次倍,Innodb默认是16k。

        所以我们要做的是,减少IO的次数,减少IO的数据量。

        所以少用select * 减少数据的 访问次数

用int好还是varchar好:越短越好。越短的话,树就会越低。

ID索引,要不要自增:要自增,这样避免了页分裂和页合并。提高了效率。

MySQL高可用:减少不可用时长。单点故障,SQL满了,认为,网络。搭建主从集群。一主一从架构可以变,读-B,写-A。A--binlog  写到B库中,做到同步

3、MySQL聚簇索引和非聚簇索引的区别(定义,innodb,叶子)

        聚簇索引就是索引和数据存储在一起,行数据直接存储在叶子结点上。innodb中聚簇索引默认是主键,一张表只能有一个聚簇索引;非聚簇索引又叫二级索引,索引和行数据分开存储,在innodb中,叶子结点存储的是索引值和主键值,通过二级索引获取行数据,需要先获取主键的值,去聚簇索引上去查询具体的行数据,叫回表。

        二级索引不直接存储行数据的地址:减少数据变更导致全部行数据地址的维护成本。

Innodb数据文件。.frm和.ibd,数据和索引放在一起的。当插入数据的时候,必须将数据跟某一个索引列绑定到一起,索引列可以是主键,也可以是唯一键也可以是6字节的rowid。

6字节rowid不够存怎么办?int是4字节到21亿范围,需要分表了,不然查询太慢了。

分库分表技术:mycat,shardingsphere(推荐)阿里云DRDS

一个表里面可以有多个索引列,数据文件有且只存储一份,不会造成冗余。其他索引列如何解锁数据,会将跟数据绑定到一起的索引列的值放到其他索引列的子节点。

Innodb中既存在聚簇索引,有存在非聚簇。

MYisam中只有非聚簇索引,叶子结点只放地址。

SQL响应慢,有哪些排查思路:

SQL语句优化,架构涉及优化,缓存存储:

        SQL语句:基于慢查询日志,基于explain分析SQL语句的执行情况,保证每次查询命中索引,避免全盘扫描,提升效率。

        架构层面:表设计层面,分库分表,比如字段非空约束,字段用比特等占空间比较少的数据类型,也可以做一些反范式的设计,单表数据量500w以上,即便命中索引,查询也是很慢,可以水平分表,把500w数据拆分成两张表或者更多,提升效率;也可以垂直分表,基于业务进行拆分;一台服务器是存在性能瓶颈的,可以作读写分离,搭建主从架构,一主多从,主写从读,

        热点数据做JVM缓存或者Redis缓存。

java集合

        set,list,queue(FIFO队列),map。

        从JDK 1.5开始,并发包下新增了大量高效的并发的容器,这些容器按照实现机制可以分为三类。第一类是以降低锁粒度来提高并发性能的容器,它们的类名以Concurrent开头,如ConcurrentHashMap。第二类是采用写时复制技术实现的并发容器,它们的类名以CopyOnWrite开头,如CopyOnWriteArrayList。第三类是采用Lock实现的阻塞队列,内部创建两个Condition分别用于生产者和消费者的等待,这些类都实现了BlockingQueue接口,如ArrayBlockingQueue。

4、使用MySQL索引有什么原则

语句:select * from table where name = "zhangsan" 

回表:先找name,从B+树找到ID值,再去主键B+树取出全部数据。叫回表。根据ID去聚簇索引中获取全量记录。

索引覆盖:从索引的叶子结点查询列的过程,尽量减少回表。

最左匹配:(组合索引)MYSQL优化器回选择合适的顺序执行。最左,按照的是创建顺序。

索引下推:MySQL5.7以后默认支持索引下推,把server层的数据过滤下推到搜索引擎层。

41、MySQL索引的优点和缺点。

B+树,IO;范围查找,叶子链表;唯一索引约束;索引维护性能开销不好。

MySQL性能优化?4部分,硬件和操作系统,架构,配置,执行。

1、DBA或运维 2、架构,搭建主从集群,或者主主集群。读写分离设计,分库分表机制,分库降低单个服务器节点的IO压力,分表降低单表数据量,提升SQL查询效率。热点数据引入高效的分布式数据库。3、my.cnf配置文件,最大连接151,可以修改;binlog日志默认不开启,可以手动开启

client------server-------存储引擎:

server层:连接器、分析器、优化器、执行器;

5、不同的存储引擎是如何实际存储的

6、MySQL组合索引的数据结构是什么样的?

7、MySQL索引如何进行优化

当表中的全部字段都是索引列的时候,无论进行什么样的查询都会用到索引。

索引不是越多越好,也不需要全部加上索引。

如果从当前索引里数据都有了,不需要主键那棵树了。可以直接从主键那棵树返回全量的数据。

8、JDBC:加载驱动,打开链接,执行代码,返回结果,关闭连接。

9、防止SQL注入

        使用preparedStatement类不用statement类;预编译;规定输入的数据长度;参数过滤。等。

4、虽然Redis是内存存储,但也可以支持持久化,避免服务器故障丢失数据。

10、Redis快的原因是什么?

1、基于内存实现的数据库,没有了磁盘IO的限制。进行内存IO操作的时候,可以达到很高的QPS,官方提供指标是10w。

2、有高效的数据结构。String,List,hash,set,zset,不同的数据结构采用了不同的编码类型。

3、合适的线程模型。Redis底层是单线程的数据IO,因此不需要在算法层面考虑并发的安全性,底层算法的时间复杂度基本都是常量级别。

I/O多路复用模型同时监听客户端;使其在网络IO操作中能并发处理大量的客户端请求,实现高吞吐率

  • I/O :网络 I/O

  • 多路:多个 TCP 连接

  • 复用:共用一个线程或进程

Redis单线程在执行过程中不需要进行上下文切换,减少了耗时。

set和zset:

        Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。set底层的存储结构包括ziplist或skiplist,

Redis单线程架构:

        Redis的单线程主要是指Redis的网络IO和键值对读写是由一个线程来完成的。而Redis的其他功能,如持久化、异步删除、集群数据同步等,则是依赖其他线程来执行的。所以,说Redis是单线程的只是一种习惯的说法,事实上它的底层不是单线程的。

        单线程可以简化数据结构和算法的实现,并且可以避免线程切换和竞争造成的消耗。

        实现Redis高可用:(机器故障、短时间访问暴增,塞入key值太多,内存过大来不及回收。做好Redis的高可用配置。)

        虽然Redis是内存存储,但也可以支持持久化,避免服务器故障丢失数据。

      主从模式,一主一从,一主多从,主节点挂了手动指定主节点,主节点写,从节点读。哨兵模式,哨兵站岗。现在一般都是集群模式,多主多从,分布式存储,数据分片,压力分摊,扩容也简单。

MySQL高可用解决方案:

        高可用按照9的个数来表示。减少MySQL不可用时长。单点故障,机器资源耗尽等。

        双十一的时候,买了个东西,insert请求写到MySQL-A,select请求到了MySQL-B中,查询订单信息的时候,数据不同步查询不到订单信息。增加主从同步的方案。写请求到主库,触发一个data change,写入一个叫bin log的日志文件中。我们可以把binlog传到从库,从库解析binlog的sql语句,就可以实现同步。从库通过IO连接到主库,要IO文件;---relylog日志中sql执行。实现一致。

        

互斥锁和读写锁的区别:

        互斥锁保证同一时间,只能有一个线程访问该对象。互斥锁不分读写。

        读写锁,分为读锁和写锁。写锁优先于读锁,同一时间只允许有一个写进程,其他写进程会阻塞,直到写释放,其他进程被唤醒,期间读锁也不能被其他进程获取。适用于读远大于写的场合。

Redis数据放在内存,效率一定很高吗?数据结构做了哪些优化?

跳跃表?

        弥补链表查询时间复杂度On而设计的,在每个节点中维持多个指向其他节点的指针,时间复杂度On变为Ologn:有序集合的底层实现,集群节点中用作数据内部结构。

生产环境,服务器变慢处理

        CPU利用率,IO效率,内存。CPU--top命令找进程,jstack命令找线程。IO的效率问题,我们可以借助缓存系统,减少磁盘IO的次数。顺序写替代随机写,减少寻址的开销。内存瓶颈,JVM里面避免频繁的GC,考虑内存泄漏的问题,也可以根据垃圾对象的特点进行参数的调优,使用更合适的GC垃圾回收工具。

谈谈你对行锁,临键锁,间隙锁的理解。

        (都是保持数据一致性的排他锁)行锁,对主键修改的时候,锁定一条记录防止被修改;间隙锁,锁定索引左右开区间;临键锁,非唯一查询,既包含了索引记录,又包含了索引区间。总的来说都是表示了数据的范围,最终解决的都是幻读的问题。非唯一索引匹配的时候,默认加一个临键锁。所以尽可能使用主键索引和唯一索引进行查询,避免大范围锁定影响性能。

11、MySQL事务和一致性问题。

MySQL调优:

12、涉及多张表的时候,怎么保证数据的一致性。

13、数据库性能处理,分批,异步

14、搭建一个集群,把耗时的放到一个队列去,生成很多消费者。

15、项目的并发性,能到多少用户同时访问。

16、数据库的IO瓶颈。

17、mq,kafuka

18、数据库性能调优。并发查询,内存占用。

19、分库分表

20、分布式集群,一个请求,代理,转发这些随机转到节点上去。

22、使用枚举类创建对象,可以防止反射攻击。只要有反射访问,任何代码都不安全。SetAccessable设置为True,实现反射访问。

23、Redis多线程模型理解:6.0的多线程,不是指令操作的多线程,而是网络IO的多线程。Redis瓶颈,网络,CPU,内存,主要是网络和内存,6.0解决网路IO性能

23、DCL单例模式为什么要volatile修饰实例对象

        双重检查锁的单例模式下,存在不完整对象的问题。instance = new DCLExample();new不是原子性的,先分配内存空间,在初始化,在实例对象赋值给instance对象。所以要加volatile修饰,防止指令重排序。

24、hashMap为什么右移16位异或运算。hash值的散列度更高。无符号右移16位,putvalue方法是取模运算,集中在几个位置,而右移16位hash值的高位移动到了低位,相当于高位和低位特征组合了。散列度更高了

26、MySQL事务的实现原理。

        ACID特性,Innodb如何保证ACID特性。

A,原子性;undolog;一致性:主要是依赖于业务层面的保证,数据库本身也提供了主键唯一约束,字段长度和类型的保障等等。

        i表示事务的隔离性,Innodb里面使用SQL92标准,提供事务四中隔离级别,未提交读,已提交读,可重复读,可串行化。默认可重复读,MVCC解决脏读和不可读,使用行锁和表锁就觉幻读的问题;

脏读、幻读 数据库锁(行锁,表锁,共享锁,排他锁)脏读、不可重复读、幻读和事物隔离级别_zhengliang1028的博客-CSDN博客

        D持久性,直接持久化到磁盘,先更新到缓冲区,中途宕机丢失,引入了redolog,把redolog的日志刷新到数据库里面。MVCC,行锁,表锁,redolog,undolog特性来进行保证。

        MVCC多线程并发安全问题的无锁并发控制技术。读读并发(无问题),读写并发(脏读幻读不可重复读),写写并发(新数据丢失)。

在读操作的时候,不用阻塞写操作,写操作的时候,不用阻塞读操作。

实现读写一致性,解决脏读幻读,不可重复读,不能解决数据更新的丢失问题

乐观锁或悲观锁来解决写和写的冲突。

MVCC实现核心:

        1、表的隐藏列,记录事务id和上版本数据地址

        2、undolog:各版本修改历史,即事务链

        3、readView读视图,用于判断那些版本可见。

27、MQ保证消费顺序。两个方面,kafka使用partion分片机制,自定义消息路由算法。异步消费(pass)kafka只能保证一个分区的消息,硬要保证一个分区,消耗有点大。

28、SpringBoot如何自动装配,减少配置。

1、从本质上来说,Spring Boot就是Spring,它帮你完成了一些Spring Bean配置。
2、Spring Boot使用“习惯优于配置”的理念让你的项目快速地运行起来
3、但Spring Boot本身不提供Spring的核心功能,而是作为Spring的脚手架框架,达到快速构建项目的目的

        Spring Boot优点, 可以快速构建项目 - 可以对主流开发框架的无配置集成 - 项目可独立运行,无需外部依赖Servlet容器 - 提供运行时的应用监控 - 可以极大地提高开发、部署效率 - 可以与云计算天然集成

抖音找的面试题标答

CMS老年代垃圾回收器:

        多线程,CMS减少STW:首先标记清除拆分四个阶段:初始标记,GCroot(静态变量,常量,局部变量)(暂停时间短)直连对象,效率高;并发标记:GCroots连接的标记出来,对象里面的数组,map列标等,耗时比较长,不暂停用户线程,(产生变动,后面重新标记);重新标记:修正,暂停所有用户线程,只看修改的标记。第四阶段:并发清理,不任何暂停。现在用户感知不到。响应时间优先。并发清理的时候没影响,因为用不到垃圾了,存活对象不移动的。

问题1:CPU敏感,CPU核心数并发(CPU核超过4个,才好用)

问题2:浮动垃圾,浮动垃圾要到下一次才可以扫描,内存要预留一点给浮动垃圾。

问题3:内存碎片,清理后,剩余内存分散区域,分配于各大对象分配不了。垃圾回收器退化成serial Old 单线程的标记整理算法(非常卡顿)

1、描述Redis的缓存淘汰策略

Redis是基于内存的K-Value存储中间件。

Redis缓存淘汰策略(内存淘汰算法):3个方面。

1.当Redis使用内存达到maxMemory阈值的时候,Redis根据配置的内存淘汰策略吧访问频率不高的Key从内存移除掉。maxMemory默认值是服务器的最大内存。

2.Redis提供8种缓存淘汰侧率,归类为5种:LRU策略,不经常使用的淘汰;LFU,在LRU上优化,进一步确定淘汰的是非热点的(热点key:访问频率很高,缓存击穿,打到DB上,引发业务雪崩---使用二级缓存,key分散到不同的服务器--核心和非核心业务做Redis隔离);随机策略,随机删除一些key;ttl策略,挑选过期时间的key里面的更早时间的key删除。直接报错。我们可以在Redis.config文件里面手动配置。

3.使用缓存的时候,建议增加缓存的过期时间,我们知道缓存的大概生命周期,从而更好的利用内存。

2、任务数量超过线程池的核心数量,如何让其不进入队列而直接启用最大线程数。pass

        预热核心线程,添加阻塞队列,添加失败则创建非核心线程增加处理效率;非核心线程到了阈值,拒绝策略。 只需要影响第二步即可。java线程池构造方法里面有个参数可以修改阻塞队列的类型,我们把线程池的阻塞队列参数替换成SynchronizeQueue就可以了,避免任务进入阻塞队列,而直接启动最大线程数。

3、CPU飙高反应慢怎么排查。

        FullGC,挖矿,死循环,线程过多,线程是最小的单元,找进程,定位到线程,使用top指令。

4、为什么用Spring框架。简化开发:IOC和DI,帮我们管理了Bean的生命周期,实现了对象之间的松耦合。AOP,把业务逻辑和系统相关的服务分离开。

5、线程池如何实现线程的复用的?阻塞队列实现生产者消费者模型。

        线程池里面采用生产者消费者模式实现线程的复用。生产者消费者模型其实就是通过一个中间容器去解耦生产者消费者任务处理的一个模型,生产者生产任务保存到容器里面,消费者从容器里面不断消费任务。线程池里面,有任务的时候去执行,没有任务就等待并且释放CPU资源,所以使用的数据结构是阻塞队列。生产者线程生产并提交任务到阻塞队列,线程池里面阻塞线程不断从阻塞队列获取任务去执行。阻塞队列没有任务,就会阻塞等待,有新任务进来的时候工作进程在唤醒。实现了线程的复用。

6、分布式ID设计方案:Redis原子递增,MySQL的全局表,java的UUID生成全局id,解决id唯一性,实际生产中,全局唯一的ID,有序性(B+树范围查找),安全性(恶意爬取),可用性、性能(业务需求,亿级别的调用的话,性能的要求比较高)假设选择数据库的全局表作为全局id的生存系统,那么每获取一个id,就会更新一次数据库,性能上限是很明显的。现在主流的是雪花算法,64位长度组成的全局id生成算法。位运算,性能挺好的。

7、happensBefore的理解。描述内存可见性模型。描述两个操作的内存可见性。一个操作对另一个操作可见,那么必然存在happensbefore的关系。存在很多happensbefore规则,顺序规则,传递性规则,volatile规则等具体不是很了解。

8、java官方提供哪几种线程池?5种不同线程池的创建方式。

        newCachedThreadPool大量的短期的线程池、

        newFixedThreadPool固定线程数量的。处理不过来的时候阻塞队列、

        newSingleThreadPool只有一个线程数FIFO、

        newScheduleThreadPool延迟执行,定时调度、

        newWorkStealingPool  jdk8 加的。

9、线程池两次调用start()方法,出现啥问题?线程的生命周期。第二次调用start的话会报错。线程运行的时候,会先判断当前线程的运行状态。从逻辑还是实际运行都是不合理的。

线程的生命周期:新建,就绪,运行,阻塞以及死亡

10、聊聊自己熟悉的设计模式:23种设计模式。单例模式,代理模式,工厂模式等,原型模式,策略模式,装饰器模式等。

public class Singleton{
private static Singlrton obj = null;
private Singleton(){
}
public static Singleton getInstance(){
if(obj==null){
    Synchronized (Object.class){
        if(obj==null){
            obj = new Singleton();
}
} 
}
return obj
}


public static void main(String[] args){

    Singleton A = Singleton.getInstance();

    Singleton B = Singleton.getInstance();
    System.out.printlt(A==B);
}

}

        创建型包括:单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式; 结构型包括:代理模式、装饰模式、适配器模式、组合模式、桥梁模式、外观模式和享元模式; 行为型包括:模板方法模式、命令模式、责任链模式、策略模式、迭代器模式、中介者模式、观察者模式、备忘录模式、访问者模式、状态模式和解释器模式。

12、java的文件拷贝方式有几种?java io下的库,fileImportStream,FileOutputStream;java.nio包,java本身的File.Copy。和操作系统有关系。

14、AQS为什么双向链表:

        三个原因:线程阻塞之前需要判断前置节点的状态,如果没有指针指向前置节点,就需要从head节点开始遍历,性能非常低。

        第二个原因:线程可以被中断,是不需要去竞争锁的,但是它仍然存在于双向链表里面,单向链表需要从头结点遍历查找中断线程并排除

        第三个,没有获取到锁的线程会尝试自旋,也需要判断前置节点是不是头结点。对前置节点的查找,单向链表会很复杂。

15、强引用,弱引用,软引用,虚引用。不同的引用类型主要是对象不同的可达状态和对垃圾收集的影响。

        强引用:new创建的,永远不回收。

        弱引用,一定被回收,只能到下个垃圾回收节点。

        软引用,内存空间不够用的时候会被回收。

        虚引用,只是被回收的时候,系统通知,被引用时被任何影响。

17、索引什么时候失效?Innodb里面索引引擎,主键索引,普通索引。B+树存储索引数据。索引失效的情况:在索引列上做函数运算。按照最左匹配原则,从索引的最左列开始顺序检索,否则不会走索引;隐式转换的时候;索引列使用≠或者not的时候;like不符合最左匹配,不走索引;or左右不全索引。Explain命令看是否走索引。

18、线程池的线程回收。(核心线程可以回收吗,线程池的shutdown方法会发生什么事情:安全关闭线程池)

线程池分为核心和非核心线程。当线程池里面的队列满了,增加非核心线程提高任务处理能力。任务处理完,非核心线程可以及时回收。一般不回收核心线程,线程池本身就是为了实现线程的复用。核心线程在空闲的时候是处于阻塞状态,并不占用CPU资源。

19、MySQL的事务实现原理。ACID,原子性,一致性,隔离性,持久性。原子性主要是通过undo log逻辑日志来实现的,对数据库的增删改查操作,都存放在undo log里面,如果操作成功了一部分,可以通过日志回溯,撤回,达到全部失败的目的。一致性是通过redo log来实现的,Innodb将增删改查先记录在redo log里面,刷新内存,选择合适的时候再写入到磁盘。undolog和redolog不是先写到磁盘里面,而是先写到logbuffer在同步到OSbuffer里面。undolog是逻辑日志,redolog是物理日志。如果断电,MySQL提供三种方式保持持久性。重启之后先启用redolog,把写入到OSbuffer还没来得及写到磁盘的更欣赏,保证一致性。在执行undolog 进行事务回滚。保证数据的原子性。

20、CAS实现原子性。总线锁定,缓存锁定。总线锁定是CPU使用总线锁,CPU在总线上输出lock信号时,其他线程被阻塞。缓存行是CPU高速缓存的最小的基本单位。缓存锁定是因为总线锁定的开销太大,CPU对缓存进行锁定。

21、AQS底层双线链表?一个指向前驱,一个指向后继。没有竞争到锁的线程放在阻塞队列,存在null的时候,可以逆着来。没有获取到锁的线程,阻塞队列允许中断,否则无法唤醒其他节点。自旋竞争锁,但只有一个头节点,单向链表无法查找前置节点。

AQS:多线程同步器,是juc包中多个组件的底层实现,lock锁等。AQS提供了两种锁的机制:排他锁(只能有一个线程拿资源)和共享锁(读锁)。比如Lock中的reentrantlock重入锁用到的就是AQS的排它锁的功能。读锁允许同一时刻多个线程同时获取锁资源,countdownlanch和semphone,

        互斥变量设计以及多线程同时更新互斥变量时候的安全性;未竞争到锁资源的线程的等待和竞争到锁资源释放后唤醒,锁的公平性;AQS用了一个int类型state表示锁竞争的状态,0无资源,>=1表示占用到锁。获取锁资源的时候首先判断state是不是等于0,是的话修改为1,表示拿到锁,但存在安全性,所以AQS用CAS机制保证state变量更新的原子性。未获得锁的线程进入阻塞,阻塞的线程加入到双向链表的头部,释放锁之后,会从双向链表头部唤醒下一个等待的线程。非公平锁是不管有没有等待线程,都会尝试修改state去竞争锁。

java那些地方用到了CAS:

        原子类、AQS、并发容器。

        原子类,以Atomic Integer为例,它的内部提供了诸多原子操作的方法。如原子替换整数值、增加指定的值、加1,这些方法的底层便是采用操作系统提供的CAS原子指令来实现的。 

        AQS,在向同步队列的尾部追加节点时,它首先会以CAS的方式尝试一次,如果失败则进入自旋状态,并反复以CAS的方式进行尝试。此外,在以共享方式释放同步状态时,它也是以CAS方式对同步状态进行修改的。

        并发容器,以ConcurrentHashMap为例,它的内部多次使用了CAS操作。在初始化数组时,它会以CAS的方式修改初始化状态,避免多个线程同时进行初始化。

        CAS的实现离不开操作系统原子指令的支持,Java中对原子指令封装的方法集中在Unsafe类中,包括:原子替换引用类型、原子替换int型整数、原子替换long型整数。这些方法都有四个参数:var1、var2、var3、var4,其中var1代表要操作的对象,var2代表要替换的成员变量,var3代表期望的值,var4代表更新的值。

22、Redis的线程模型。6.0以后,哪些地方用到线程,是否安全等等。

23、concurrenthashmap的size方法是不是线程安全的?是的。实现是安全的,调用方法获取线程个数的时候,不是线程安全的。put和size方法没有实现同步锁,当前添加的元素获取不到。

24、千万级数据,数据库怎么?基本的关系信息放数据库,数据库有瓶颈的话,通过分表等进行优化,分页加载。抖音的关注列表,是分页查询,我觉得可以用NoSQL存储,之前微博是搭建的百万级别的Redis集群来存储关注信息。数据量很大的话,加一个非结构化数据的存储来保存,其实是保存的少部分,下拉分页加载的。对事务数据的分析,非关系型数据库,再做一次数据存储。

25、库存超卖问题,加锁,分布式锁等。量不大的话,Redis来做,Redis需要原子性操作。Redis本身就是原子性操作。

26、时间轮。存定时任务的环状数组。

27、分布式和微服务的理解。微服务本身就是一种分布式。单体业务放到多个服务器进行计算。

28、JVM分代年龄为什么是15次,可不可以是25次。

29、MySQL的事务隔离级别?

        读未提交(啥也不解决);读取已提交(解决脏读);可重复读(默认,解决脏读和可重复读,要保证ACID的隔离性);串行化(解决全部问题,但性能最低)。

        在InnoDB存储引擎在可重复读事务隔离级别下,使用Next-Key Lock的锁算法,因此避免了幻读的产生。

        为了解决不可重复读,MySQL 采用了 MVVC (多版本并发控制) 的方式。 我们在数据库表中看到的一行记录可能实际上有多个版本,每个版本的记录除了有数据本身外,还要有一个表示版本的字段,记为 row trx_id,而这个字段就是使其产生的事务的 id,事务 ID 记为 transaction id,它在事务开始的时候向事务系统申请,按时间先后顺序递增。

30、ArrayList的自动扩容原理。默认10个,可以指定长度,自动扩容,创建新数组,1.5倍,老数组拷贝,扩容完成后,添加新元素。

31、Thread和Runnable的区别?类,后者接口,实现多继承;Runnable是一个顶级接口,Thread类是实现Runnable接口,都需要实现run方法;Runnable定义一个具体任务,Thread才是真正的处理线程。thread实现了Runnable接口并做了线城实现的扩展。

32、一致性Hash算法。分布式环境下,哈希表动态扩容的问题。节点改变的时候,要大量数据迁移。一致性算法,解决这个问题。落到hash环的位置,少部分数据迁移就可以了。一致性的好处,扩展性很强。

33、blocked 和 waiting的区别。阻塞等待;没有竞争到Synchronize的时候阻塞等待 ,就是blocked。

最大区别有两个:一个是blocked是竞争锁失败后被动触发的,waiting是自动触发的;blocked唤醒是自动的,waiting唤醒是主动的。

34、cookies 和session的区别?

        cookies是客户端浏览器来保存服务器端数据的一种机制。服务器将一些状态数据,k-value的形式写到客户端浏览器。下次访问时,识别使用者。session表示一个会话,是服务器端的容器对象,本质上可以认为是concurrentHashMap,存储当前会话的状态数据。http协议是无状态协议。session弥补其不足。session和cookies我们可以做成有状态的HTTP协议。

(会,大,安,时)

 1、Cookie和Session都是会话技术,Cookie是运行在客户端,Session是运行在服务器端。
 2、Cookie有大小限制以及浏览器在存cookie的个数也有限制,Session是没有大小限制和服务器的内存大小有关。

 3、Cookie有安全隐患,通过拦截或本地文件找得到你的cookie后可以进行攻击。

 4、Session是保存在服务器端上会存在一段时间才会消失,如果session过多会增加服务器的压力。

35、spring的事务和分布式事务:完全是两个概念。spring中没有提供事务,本质上是数据库事务的管理的封装,我们只聚焦于业务的开发即可。事务管理主要是针对单个数据库里面的多个数据表。要满足事务的ACID特性。传统的关系型数据库不支持跨库的事务操作,引入分布式事务。Spring里面没有提供分布式事务的场景支持。

循环依赖:互相依赖,三者间依赖。自我依赖。spring设计了三级缓存解决bean的循环依赖。先一级在二级缓存。总结:spring一级缓存存放成熟的bean,二级缓存存放早期bean,先一级在二级。三级缓存代理bean,代理工厂创建的bean,最后复制到一级缓存。循环依赖无法解决:多实例bean setter注入,构造器注入,单利的代理bean  setter注入。

36、Reentrantlock怎样实现公平和非公平。没有竞争到锁的线程加到AQS同步队列,队列是FIFO的双向链表。公平的:线程在竞争锁资源的时候看看AQS里面是不是有等待线程,加入尾部。非公平:不管有没有线程等待,都会尝试抢占锁资源。synchronize和Reentrantlock都是非公平的,考虑到性能的原因。非公平锁的时候,不用到内核态的切换,可以直接竞争到锁。

37、阻塞队列。在队列的基础上增加了两个附加操作。队列空的时候,获取元素的线程会等待队列变为非空,队列满了的时候,存储元素的线程等待队列变为可用。很容的实现生产者消费者模型。

38、wait和notify为什么要在synchronize代码块?wait阻塞,notify唤醒,组合使用,多线程通信,共享变量实现。Synchronize实现多个线程通信之间的互斥。jdk强制要求wait 和notify写在同步代码块。

39、kafka保证消息不丢失。

40、缓存雪崩和缓存穿透,以及如何避免。

        缓存雪崩:大量数据同时过期、或是redis节点故障导致服务不可用,缓存层无法提供服务,所有的请求直达存储层,造成数据库宕机。解决方案:1.避免数据同时过期,设置随机过期时间。2.启用降级和熔断措施。3.设置热点数据永不过期。4.采用redis集群,一个宕机,另外的还能用

        缓存穿透:是指客户端查询了根本不存在的数据,使得这个请求直达存储层,导致其负载过大甚至造成宕机。这种情况可能是由于业务层误将缓存和库中的数据删除造成的,当然也不排除有人恶意攻击,专门访问库中不存在的数据导致缓存穿透。 我们可以通过缓存空对象的方式和布隆过滤器两种方式来解决这一问题。缓存空对象是指当存储层未命中后,仍然将空值存入缓存层 ,当客户端再次访问数据时,缓存层直接返回空值。还可以将数据存入布隆过滤器,访问缓存之前以过滤器拦截,若请求的数据不存在则直接返回空值。

        缓存击穿:当一份访问量非常大的热点数据缓存失效的瞬间,大量的请求直达存储层,导致服务崩溃。 缓存击穿可以通过热点数据不设置过期时间来解决,这样就不会出现上述的问题,也可以通过加互斥锁的方式来解决缓存击穿。

穿透,击穿,雪崩。

42、Innodb如何解决幻读。

        幻读,对相同范围的查询结果不一致。设置间隙锁,查询范围加锁,对B+树的范围加间隙锁,大范围的时候,多个,next_key lock多个索引区间锁定。

43、不加锁,解决线程安全(java的CAS机制实现)。多线程对同一资源的访问,导致原子性、可见性、有序性问题。一般是加同步锁。根本原因是并行共享资源。无锁并发:自旋锁,乐观锁(版本号,CAS机制),业务上减少共享对象的使用。

乐观锁

        乐观锁总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量。

悲观锁:

        悲观锁总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。

44、解决哈希冲突的方法:

        链地址法(对于相同的哈希值,使用链表进行连接。(HashMap使用此法)),

        再哈希(多个哈希函数,冲突了在哈希),

        建立公共溢出区(将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。)

        开放地址法。

第二部分:java基础,OOP

1、重写、重载、封装、继承、多态

继承,已有的类派生新的类。封装,同类事务特征封装在一起,只对外暴露接口,减少代码耦合。

public,protected,default,private

        private、default、protected、public; 被访问修饰符的成员访问 --> private只能在当前类的内部被访问,default可以被相同包下的其它类访问、protected可以被同一个包下的访问、public无论访问类和被访问类在不在一个包下,有没有父子关系,这类成员都基于被访问到

        构造方法不能重写,因为类名要一致。

        多态:同一个行为具有多个表现形式。编译时多态,重载。运行时多态,继承

2、this

3、object类方法:notify,notifyall,wait,toString,hashcode,equals等

String:

        创建字符串有两种方式,一种是使用字符串直接量,另一种是使用new关键字,当使用字符串直接量的方式来创建字符串时,JVM会使用常量池来管理这个字符串,当使用new关键字来创建字符串时,JVM会先使用常量池来管理字符串直接量,再调用String类的构造器来创建一个新的String对象,新创建的String对象会被保存在堆内存中。

4、接口:不能实例化,接口实现了java的多继承。抽象是对类的抽象,接口是对行为的抽象。

        抽象类和接口。抽象类的目的是为了实现代码的复用,多个类具有相同的行为的话,可以抽象出来抽象类作为父类,子类继承父类并实现自己的扩展。所以抽象类不能被实例化,只能被继承,关键字就是public(默认)和protected。抽象类是对类的本质的抽象,是is a(是不是) 的关系。接口的目的是为了约束类的行为,因此不能规定类不能干什么事,只能规定能干什么。是对行为的抽象,是一种like a(有没有) 的关系。接口的核心是定义行为,对于行为具体怎么实现的,不关心。

        抽象类和接口的区别:有默认构造器,不能有构造器;抽象类除了不能实例化,和普通类没什么区别,接口是完全不同的类型。抽象方法可以public,protected,default,接口只能是public。

        相同点:1、两者都不能实例化;2、可以拥有抽象方法。 区别:1、抽象类定义的关键字是abstract class,接口定义的关键字是interface;2、属性上,抽象类可以有静态变量、常量和成员变量,接口只能有常量;3、抽象方法可以有普通方法,而接口jdk1.8之前只能有抽像方法(1.8之后,增加了静态方法和默认方法);4、抽象方法可以有构造方法,接口不可以有构造方法。5、一个类只能单继承一个父类,而一个接口可以继承多个父接口,同时,一个类可以实现多个接口却没有实现多个父类这一说法。

5、static:静态的,类名.来加载,不加static是类独有的方法,加了的话在JVM加载时分配,且只分配一次,多个静态代码块则按顺序执行。

        Java类中包含了成员变量、方法、构造器、初始化块和内部类(包括接口、枚举)5种成员,static关键字可以修饰除了构造器外的其他4种成员。

类成员不能访问实例成员,因为先加载类成员。

        static修饰的部分会和类同时被加载。被static修饰的成员先于对象存在,因此,当一个类加载完毕,即使没有创建对象也可以去访问被static修饰的部分。 静态方法中没有this关键词,因为静态方法是和类同时被加载的,而this是随着对象的创建存在的。静态比对象优先存在。也就是说,静态可以访问静态,但静态不能访问非静态而非静态可以访问静态。

6、final:类,方法,变量。类不能被继承,方法不能被重写,变量不能被修改。

7、静态代码块:类加载时,只执行一次

8、String,StringBuffer,StringBuilder:可变性(string final),安全性;性能;存储;是否可修改、安全、增删改查效率。

        StringBuffer提供的append()、insert()、reverse()、setCharAt()、setLength()、等方法来改变这个字符串对象的字符序列。

9、创建线程的方法:3个,继承Thread类,实现Runnable接口,实现callable接口。继承Thread类重写run方法,.start启动;自定义类实现runnable接口,重写run方法,创建实现类,把自定义类当成参数传进去,.start启动;callable接口,有返回值和抛出异常。

10、start和run。start开启新线程,调用run方法启动线程,run只是一个普通类而已。

11、处理线程同步的三个方法:同步代码块,同步方法,可重入锁ReentrantLock。

12、数组、链表、队列、堆、栈、树、图、散列表。

13、ArrayList:基于数组,内存连续,增删改查效率访问O(1),初始容量10,扩容1.5倍

        ArrayList是基于数组实现的,它的内部封装了一个Object[]数组

14、LinkedList:基于链表,内存不连续,增删改查效率。占内存大小。

        新增数据:无限内存,.next。

        根据key计算index是散列的无序的。如果要有序的话,linkedhashmap双向链表。

15、HashSet:jdk1.7,数组+链表,jdk1.8数组+链表/红黑树,增删改查效率,初始容量16,0.75因子,2的倍扩。0.75原因:因为HashMap的底层数组长度因为是需要2^n,默认是16,数组离散情况下的泊松分布,时间和空间成本上的折中,设置为0.75相对合适,0.75的话,转化红黑树的概率几乎为0。1的话冲突太多,0.5的话浪费内存空间。hashmap扩容牵扯到数据迁移,指定大小创建数组最好。

hashmap如果没有冲突,复杂度O1,存在链表ON,红黑树Ologn

hash散列计算,无序的,要是有序的话,可以使用linkedHashMap有序。

hashmap1.7数组+链表。1.8以后,数组+链表/红黑树。数组容量大于64,链表长度大于8自动树化。

        put方法:1.7是entry,先判断长度if扩容(长度*装填因子),在头插法;1.8是node,先尾插法,在判断长度是否红黑树化。

        hashmap1.7扩容死循环问题:jdk官方不认为这是bug,因为hashmap jdk官方本就不建议在多线程情况下适用,1.7之前使用的是头插法,认为后插入的读取的概率更高,resize后可能形成环从而造成死循环。1.8以后使用了尾插法,避免了死循环的问题。多线程条件下使用ConcurrentHashMap来处理问题。

扩容是2倍的原因:容量是2的幂次时,与添加元素的hash值做位运算的时候能够充分散列,比较均匀的分布在hashmap的位置上。减少hash碰撞。

        ConcurrentHashMap:1.8以前是16个区段的segment数组(分段锁),每个区段保存若干个散列桶,每个散列桶的长度扩容成2的幂次。多个线程操作同一个segment的时候,操作会上锁,其他线程则会阻塞。ConcurrentHashMap让各线程操作不同的segment,可以保证线程的并发安全。1.8以后进行了改进,没有segment了,锁的粒度是一个node节点,性能变高了,数组+链表+红黑树+乐观锁(桶是空的乐观锁,获取版本号)+Synchronized(桶不为空);多线程并发扩容。

        一、ConcurrentHashMap的底层数据结构与HashMap一样,也是采用“数组+链表+红黑树 二、采用锁定头节点的方式降低了锁粒度,以较低的性能代价实现了线程安全。 三、实现机制:1. 初始化数组或头节点时,ConcurrentHashMap并没有加锁,而是CAS的方式进行原子替换 2. 插入数据时会进行加锁处理,但锁定的不是整个数组,而是槽中的头节点。所以,ConcurrentHashMap中锁的粒度是槽,而不是整个数组,并发的性能很好。 3. 扩容时会进行加锁处理,锁定的仍然是头节点。并且,支持多个线程同时对数组扩容,提高并发能力。 4. 在扩容的过程中,依然可以支持查找操作。

        ConcurrentHashMap实现线程安全的难点在于多线程并发扩容,即当一个线程在插入数据时,若发现数组正在扩容,那么它就会立即参与扩容操作,完成扩容后再插入数据到新数组。

hashmap和HashTable的区别:父(AbstractMap,Dictionary)空安遍容哈

        HashMap可以使用null作为key,不过建议还是尽量避免这样使用。HashMap以null作为key时,总是存储在table数组的第一个节点上。而Hashtable则不允许null作为key。
  3、HashMap继承了AbstractMap,HashTable继承Dictionary抽象类,两者均实现Map接口。
  4、HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75。
  5、HashMap扩容时是当前容量翻倍即:capacity*2,Hashtable扩容时是容量翻倍+1即:capacity*2+1。

HashTable是给了整个表上了个大锁,效率慢,concurrenthashmap使用的是segment+hashentry分段锁技术,保证了安全性和并发性。

hashmap解决hash冲突?

16、hashset,hashmap,treeset,treemap:根本区别是:set集合类是对map类的封装。直白一点:hashmap是键值对,hashset只有键,没有值。tree也是一样的。

17、IO流:按照功能的话:输入流,输出流,按照类型的话:字符流,字节流。字节流是8字节ASC编码传输的,字符流UTF编码,支持中文的。字符流是16位输入输出。

18、反射:

        运行的时候动态获取类的信息,方法,构造方法,字段,注解等,在反射时,也可以生成动态代理类对象。优点灵活,缺点是效率低很多。比如使用JDBC创建数据库连接时,使用反射机制加在驱动;AOP面向切面编程,是程序运行时创建目标对象的代理类,使用反射机制实现的。

getClass();xx.class;Class.forName("xxx"); 

        

19、锁:只有锁对象才能调用wait和sleep方法。

20、锁:Synchronize是基于monitorEnter和monitorExit来实现的,monitor对象是同步的基本实现单元,本质其实就是修改monitor对象头的MarkWord标志位的数值来和操作系统的锁进行对应。不管是显示同步还是隐式同步都是一样的,区别是,代码块通过明确的monitorEnter和monitorExit实现的,同步方法是通过ACC_Sychronized标志来隐式实现。同步代码块的同步原理,通过反编译可以看到,同步块前后会有一个monitorEnter和两个monitorExit字节码指令,同步方法反编译看到,表面上同步方法表面上不是通过monitorEnter和monitorExit实现的,常量池多出来的ACC_Synchronized标识符,java虚拟机就是根据这个标识符来实现代码的同步,调用方法的时候,如果有这个标志,执行线程获取monitor,获取后执行方法体,执行完毕后再释放monitor,ACC_Synchronized标识符其实就是代表线程执行到该方法后,隐式的调用monitorEnter和monitorExit两个命令将方法锁住。Synchronize1.6进行了优化:自适应自旋,锁消除,锁粗化,轻量级锁,偏向锁。

https://blog.csdn.net/qq_16049845/article/details/112276165JDK1.6 对锁的优化: 偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化 等技术。锁主要存在四中状态,依次是:无锁状态偏向锁状态轻量级锁状态重量级锁状态锁可以升级不可降级,即 无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁是单向的。 这种策略是为了提高获得锁和释放锁的效率。偏向锁和轻量级锁的区别偏向锁和轻量级锁都是为了:在无多线程竞争时,减少重量级锁中使用操作系统互斥量的性能消耗。轻量级锁在无竞争时使用 CAS 代替互斥量。而偏向锁则把整个同步都消除。轻量级锁https://blog.csdn.net/qq_16049845/article/details/112276165

        Synchronize是jvm中的monitorEnter和monitorExit实现同步,唤醒使用wait和notify。Lock是juc包中的lock层应用,依赖AQS实现。Synchronize非公平,Reentrantlock可以公平,也可以非公平。Synchronize是关键字,lock是普通的java类,前者可以锁代码块或者方法,lock只能锁代码块。Synchronize不会死锁,lock如果没有unlock是会产生死锁的。

21、Reentrantlock的实现原理:定义,特性,实现原理。

        Reentrantlock是可重入的排他锁,解决多线程对共享资源的竞争问题。特点是1、可重入的:获得锁的线程释放锁之前再次竞争同步锁的时候,不需要加锁就可以直接访问。2、支持公平和非公平特性。3、支持阻塞竞争锁和非阻塞竞争锁lock和trylock。

        底层实现核心技术的话,第一个是锁的竞争,通过互斥变量,使用CAS机制来实现的。第二个是没有竞争到锁的线程使用AQS来存储,底层就是双向链表来实现的,锁被释放以后,从AQS的头部去唤醒下一个等待线程。第三个是公平和非公平的特征,主要是在竞争的时候,是否判断AQS里面存在等待线程,非公平锁不需要判断。最后,关于锁的重入性,在AQS里面有个成员变量来保存当前获取锁的线程,同一个线程再来获取的时候,不会再走锁的竞争逻辑,而是直接增加重复次数。

        可重入的意思:线程或代码因为抢占资源或者中断,导致这个代码运行时中断了,执行结束后,重新回到这个代码或者线程,运行完结果不变。可重入锁是释放锁的线程在释放锁之前再次竞争同步锁。可重入性主要是避免死锁。自己等待自己。

22、CAS:方法的参数(V,A,B)v待更新的值,A预期值,B新值,V==A的时候,B更新。

在操作系统里面是用硬件命令保证原子性的。CAS没有阻塞,死锁,线程的上下文切换,解决并发执行的问题。

        CAS的缺点:只能保证一个变量的原子操作,大量并发的条件下,CUP消耗严重,存在ABA问题。解决:使用版本号。

        谈谈CAS机制:保证多线程环境下,共享变量修改的原子性。CAS是unsafe类里面CompareAndSwap方法实现的,四个参数,当前对象实例,state偏移量,当前值,期望值。使用场景,JUC里面的Atomic的原子实现;ConcurrentHashMap,AQS。

23、JVM判断一个对象是否可以被回收:1、引用计数法 2、可达性分析算法。1指向当前对象的引用次数,如果引用次数为0,则被回收,如果引用次数不是0,不能回收,简单效率高,但是存在内存泄漏的问题。循环引用不可行。2从GCRoot开始,向下搜索,找直接或者间接引用,遍历完成后,如果不可达,则被回收,可达则不可被回收。回收时JVM找所有GCRoot的时候会stoptheworld,是主流的算法。

GCRoot:栈里面的本地变量;本地方法栈中的变量;方法区的静态变量;正在运行的的线程。4部分

24、java只有值传递,没有引用传递。

25、数组一旦实例化,长度就固定了。

26、java创建对象:new,反射的newInstance() 方法,object类的clone方法,反序列化机制的ReadObject方法

27、两个不同对象有相同的hashcode:hash冲突时候

        为什么hashCode()的效率高,为什么还要比较equals()呢?有时候,不同的对象生成的hashCode值会一样,英文字母a和integer 97 的hashcode是一样的。1、为了提高效率,因为hashcode比equals快很多,先检查hashcode,hashcode不同则一定不同。

2、保证是同一个对象。如果重写了equals但没重写hashcode,这样有可能出现equals相同但是hashcode不同的情况。

定律:两个对象,做比较的时候,equals返回true的时候,hashcode必须一样,如果对象值完全不一样,hashcode有可能是相同的,也就是哈希冲突,英文字母a和integer 97 的hashcode是一样的。如果equals是true,但是hashcode不一样,违背了定律,将来读取内存是有可能会出现问题的,有bug隐患。

28、浅拷贝和深拷贝:浅拷贝只是复制了某个对象的引用(指针),不复制对象本身,新旧对象共享内存。深拷贝会创造一个一模一样的对象,新旧对象不共享内存,修改新对象不改变旧对象。

29、final:类,方法,变量,常量。

30、notify,notifyall:使用notify唤醒一个等待的线程,notifyall唤醒所有等待的线程,被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁,所以notify可能会导致死锁的发生。

31、sleep和wait:object.wait让线程进入阻塞状态,必须要写在Synchronize同步代码块,会释放锁资源和CPU资源,Thread.sleep只是休眠,没有加Synchronize同步锁,不会释放锁,但会释放CPU资源。在Synchronize调用sleep的时候,也不会释放锁。让线程进入阻塞的方法,CPU都会调度CPU时间片的切换,提升CPU利用率。

32、volatile:同步:

        volatile是线程同步的轻量级实现;阻塞:volatile不会阻塞,Synchronize可能会阻塞;volatile保证了可见性不能保证原子性,禁止指令重排。因为操作完立即同步到主内存,所以不是原子性。a++,b++,指令不重排的话,可能造成CPU资源的浪费。

        写内存语义:读内存语义:当读一个volatile变量时,JMM会把该线程本地内存置为无效,使其从主内存中读取共享变量。 volatile的底层是采用内存屏障来实现的,就是在编译器生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。

        内存屏障就是一段与平台相关的代码,Java中的内存屏障代码都在Unsafe类中定义,共包含三个方法:LoadFence()、storeFence()、fullFence()

33、==和equals区别。

equals底层是 this==object,比较内存地址。String是重写了equals,如果自定义java对象,比较两个java对象成员属性值,没重写equals的时候就是地址。

为什么重写了equals还要重写hashcode呢?

两个定律:两个对象做equals比较的时候,如果返回true,hashcode一定是一样的。两个对象的equals不一样,hashcode有可能是相同的,该过程是哈希冲突。a和integer97。避免违背定律的情况下,要重写的。如果只重写了equals没有重写hashcode,有可能equals是true,但是hashcode不同

        多线程,一般我们使用多线程是做异步的操作,例如接口比较耗时的情况的话,为了避免用户一直等待,把一些比较耗时的代码抽取出来,通过多线程mq来做处理。多线程异步发短信,发优惠券。多线程做异步,会消耗服务器资源,所以一般大的项目改成mq做处理操作。

        多线程保证安全问题,可以加锁,读写操作。

SpringAOP底层是用动态代理模式来实现的。

被代理类有实现接口的情况下,用的是jdk动态代理。没有实现的话,用的是cglib

说说多线程:

        线程是操作系统调度的最小单元,它可以让一个进程并发地处理多个任务,也叫轻量级进程。所以,在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈、局部变量,并且能够共享进程内的资源。由于共享资源,处理器便可以在这些线程之间快速切换,从而让使用者感觉这些线程在同时执行。

34、死锁:3个方面。什么是死锁:一组互相竞争资源的线程因为互相等待,导致永久阻塞的现象。

死锁的原因,4个:

1、互斥条件,共享资源x和y只能被一个线程占有。

2、占有且等待,线程t1已经取得的共享资源x在等待共享资源y的时候不释放共享资源x。

3、不可抢占,其他资源不能强行抢占线程t1占有的资源。

4、循环等待,线程t1等待线程t2占有的资源,编程t2等待线程t1占有的资源。

        如何避免死锁:打破四个条件的任何一个即可。互斥无法破坏。占有且等待,一次性申请占用所有资源,这样就不存在等待了。不可抢占,占有部分资源的线程进一步申请占有其他资源的时候,如果申请不到,可以主动释放占有的资源。最后是循环等待,可以按照有序申请资源,现申请资源序号小的,在申请大的,这样就不存在循环等待了。

35、hashMap如何解决哈希冲突。hashmap底层是数组结构存放数据的,数组默认长度16,put方法添加元素,根据key的hash值取模运算,保存到指定位置,可能会hash冲突,链式寻址保存到链表的尾部。链表长度>=8,数组>=64,红黑树。再哈希,布隆过滤器。开放寻址法,建立公共溢出区。

36、Synchronize锁的升级原理。1.6之前重量级。1.6之后升级。重量级是因为内核态用户态的切换。偏向锁就是直接把当前锁偏向某个线程,简单来说就是CAS机制来修改偏向锁的标记,这个适合同一线程多次去获得同一个锁资源的情况下,并且没有其他资源竞争的场景。轻量级锁可以称之为自旋锁,基于自适应自旋的机制。有线程竞争锁,先尝试偏向锁。在尝试自适应自旋,最后重量级。block状态,后期在触发唤醒。

37、SpringIOC的工作原理。IOC是什么,bean的声明方式,IOC工作流程。

控制反转,把对象的管理权限交给IOC容器。要某个实例,直接从容器里面获取。降低了对象之间的耦合。程序体系结构变得更加灵活。

配置bean有多个方式,比如xml文件里面配置bean标签,或者是@Service注解,或者是通过Configuration配置类的@Bean注解声明。Spring启动的时候会解析这些bean,保存在IOC容器里面。

工作流程分两个阶段:

        IOC容器的初始化阶段:主要是根据xml定义的或者是注解等声明的bean,解析加载后生成BeanDefinition,然后把beanDefinition注册到bean容器中。beanDefinition的实体里面包含bean的定义和属性,把这个beanDefinition保存到Map集合里面,完成IOC的初始化。IOC容器的作用就是对这些bean的信息进行处理和维护,是IOC容器控制反转的核心。

        第二个阶段是完成bean的初始化和依赖注入。第一件事是通过反射对没有设置lazy-init属性的单例bean进行初始化(调用bean的初始化方法来完成实例化,SpringIOC容器不会管理这些bean),第二个是完成bean的依赖注入,最后是bean的使用。使用@Autowired注解,或者BeanFactory.getBean()从IOC容器里面取指定bean的实例。

38、受检异常和非受检异常。受检异常是程序中无法预判的异常,编译的时候强制检查的异常,try-catch进行捕获

39、中断一个正在运行的线程。线程是系统级别的概念,由操作系统完成,JVM只是对操作系统做了一层包装而已。如果要中断一个线程,只能类似Linux的kill命令强制终止,thread里面有stop强制终止。安全中断只能在线程中提前埋好一个钩子,Thread里面提供interrupt方法,和isInterrupt方法配合使用。外部线层触发这个钩子,告诉线程可以停止。安全中断,不是强制中断。保证运行结果安全性。

40、限流算法。避免系统被压垮。计数器限流,滑动窗口限流,漏桶算法,令牌桶算法。入口前做限流。

41、new String(”abc“)创建了几个对象。new堆里面实例化字符串对象,否造访发里面传递了abc字符串对象,String是final修饰的常量,JVM去字符串常量池里面找abc的对象引用。如果abc不存在,创建2个,存在1个。

42、SpringMVC的理解。

        是对java web的MVC框架模式做了增强和扩展。传统的MVC的controller做拆分,拆分成前端控制器DispatcherServlet和后端控制器controller,把Model模型拆分成业务层Service和数据访问层Repository,视图层面支持不同视图,JSP等等。

        流程:浏览器请求---核心控制器DispatchServelet,把请求分发到controller,controller处理完业务逻辑返回ModelandView,dispatchServlet找指定的ModelandView视图,展示到客户端。

43、幂等性问题。幂等方法多次调用,和第一次执行的影响结果是相同的。网络通信里面,两种行为,导致接口被重复调用。1、用户的重复提交或者恶意攻击导致多次重复执行。2、分布式架构里面超时重试的机制,导致服务器接口被重复调用。

解决:数据库的唯一约束。Redis里面setNX命令。状态机。token或者去重表等。

要么接口只调用一次,要么对数据的影响只触发一次。

44、Mybatis里面的#{},和${}的区别。#$都是动态SQL的方式,# 等同于jdbc的?,静态占位符,可以防止SQL注入。$符号表示拼接到原始SQL里面,动态传参。

45、如何理解Springboot的starter组件。Starter组件:功能为维度,维护对应jar包的版本依赖;Starter可以导入所有的依赖jar包;内部集成了自动装配机制。完美体现了约定大于配置理念。官方是Spring-Boot-Starter前缀,第三方是后缀。约定大于配置。

47、线程池的理解?

        线程池本质池化技术,资源复用。避免每次连接和释放的开销。

        线程池创建后,默认有7个参数:核心线程数(最小线程数,不会被回收),最大线程数,任务队列,线程保持存活时间,线程工厂(创建线程),拒绝策略。

        总结:新线程进来,先看看是否够核心线程数,不够的话加入等待队列,等待队列满了的话,看看最大线程数,如果都满了,触发拒绝策略。

48、BeanFactory和FactoryBean区别。BeanFactory是spring bean的顶级接口,从容器中获取指定bean;解决bean之间的依赖,DI。FactoryBean 是工厂bean,是一个接口,动态生成某一类型的bean的实例。自定义bean加载到IOC里面。

49、Redis 存在线程安全问题吗?

        服务端:Redis本身是线程安全的k-value数据库,是安全的。6.0多线程,是处理网络IO事件的,对指令的执行过程,仍然是主线程处理的。客户端:多个客户端同时执行多个指令,无法保证原子性。原子指令。多个客户端资源访问的数据加锁。等。

        没有多线程处理指令的原因:网络IO,CPU,内存。CPU不是Redis的瓶颈点,没必要多线程执行。如果多线程,所有操作都要加锁,问题更大。

50、Spring把Bean注入到IOC容器中。7种(大概就行)。

        XML声明bean定义,启动时会解析加载;

        @CompontScan注解扫描各个类,加载到IOC容器。

        @Configuration注解声明配置类。

        Import导入配置类或者bean。

        FactoryBean动态构建bean实例。

        实现ImportSelector接口,批量注入配置类,SpringBoot自动装配机制有用到。

51、Spring中Bean的作用域有哪些。根据XML或者注解的方式告诉IOC容器,哪些bean需要被IOC容器管理。生命周期:单例模式,原型模式。 

Spring Bean的生命周期

        Bean 定义、Bean 的初始化、Bean的生存期和 Bean 的销毁4个部分

创建前准备,创建实例化(反射),依赖注入@Autowired,setter注入等,容器缓存,销毁(上下文关闭的时候)。

         这个过程是由Spring容器自动管理的,其中有两个环节我们可以进行干预。 1. 我们可以自定义初始化方法,并在该方法前增加@PostConstruct注解,届时Spring容器将在调用SetBeanFactory方法之后调用该方法。 2. 我们可以自定义销毁方法,并在该方法前增加@PreDestroy注解,届时Spring容器将在自身销毁前,调用这个方法。

52、Mybatis缓存机制。

        Mybatis里面设置了二级缓存机制提升数据检索效率。

        一级缓存是SqlSession级别的缓存,为了避免每次都查询数据库。Mybatis 把查询出来的数据缓存到sqlsession的本地缓存里面,后续命中sqlsession,直接找缓存就可以了。跨sqlsession的缓存需要二级缓存实现。

        二级缓存是sqlsession factory级别的。多用户sqlsession拿到,就放到二级缓存,其他sqlsession直接从二级缓存拿数据。

        先查询二级缓存,Redis,查不到在去一级缓存。一级缓存是jvm内置缓存,

54、为什么用Spring框架。IOC,AOP,简化开发。轻量级。MVC框架,网络编程;事务管理;功能强大。

55、java对线程安全性的理解。原子性,有序性,可见性

        使用JUC里面的  原子类、关键字volatile、使用锁 

56、双亲委派机制。

  • 启动类加载器(Bootstrap Class Loader)
  • 扩展类加载器(Extension Class Loader)
  • 应用类加载器(Application Class Loader)

        避免重复加载。保护了程序的安全性,防止核心的API被修改

57、Redis和MySQL的数据一致性问题。

        Redis是实现应用和数据库之间的读操作的缓存层,减少数据库的IO,提升性能。先尝试Redis加载,命中就返回,不命中就去数据库找。查到之后在缓存到Redis里面。就有可能出现数据不一致的问题。多表操作不满足ACID。

        不管是先写MySQL数据库,再删除Redis缓存;还是先删除缓存,再写库,都有可能出现数据不一致的情况。

延迟双删策略:在写库前后都进行redis.del(key)操作,并且设定合理的超时时间。

1、先删除缓存
2、再写数据库
3、休眠xxx毫秒(根据具体的业务时间来定)
4、再次删除缓存

58、线程池如何知道线程任务完成的。

        run完成,任务完成。可以判断线程池内部的运行状态,isTerminated方法判断线程运行状态,前提是程序主动调用shutdown方法,不好。submit方法,countdown方法。

// 59、mq保证消息不丢失:消息一定要持久化,再会消息确认机制。服务器端,同步刷盘,不丢失。持久化到硬盘。

60、数据库分库分表怎么查询?

        大表会拆分成小表,SQL语句中间件拦截,改小表名称,找where字段,分片字段,要带limit分页,不然会溢出。所以查询的时候一定加分片字段。如果不加,会查询所有的表。

        分库分表落地(余胜军):没有用哈希,用的是范围分表(可以无限扩容)。哈希分片,以后扩容很难。

61、实际开发,如何使用多线程。

        比如登录。登录后续流程,电商很复杂,赠送优惠券,短信登录提醒,等。http同步的形式,我们登录,做异步的处理,登录快速响应(消耗了CPU资源)。整合到线程池,而且一定要配置线程池名称,可以解决线程池飙高的问题。大项目以MQ做异步,完全解耦操作。

62、接口安全性。

        1、https协议,ngix做配置,ssl即可。

        2、接口做非对称加密RSA

        3、接口验证签名,MD5校验。

        4、接口做黑名单白名单。

        6、接口限流。

        7、不要暴露真实ip。带宽瞬间拉满了。

        8、防止SQL注入。

63、token模拟请求。彻底防御很难。绑定设备信息。--token限时,核心业务接口,需要刷脸,短信验证。

        分布式锁,zk,Redis,mysql,都可以实现。

Redis执行过程中,代码没执行完,key过期了咋办。正常的,可以续命,bug是线程一直阻塞,死锁,要做续命次数,主动释放锁,事务流程回滚在释放。

// mq消费过程异常,不要try,没有try抛出异常,重试,一直重试失败,

那些场景多线程:下载,批量导入,异步发邮件。

多线程发短信:失败,重试,定时任务做补偿,使用mq做异步发短信,有重试策略。

生产环境CPU飙高:代码里乐观锁cas做自旋循环;第二点可能有死循环(推出时间,或者休眠时间,Thread.sleep)bug;第三点,服务器 Redis被挖矿(显卡或者CPU)了,第四点:被攻击了。解决:先找进程,linux 用top指令,再找线程,再找代码。创建线程池的时候,命名好,这样好找。

        concurrenthashmap集合7的特点。put和get要计算两次index值。8的原理,取消分段锁设计。底层synchronize。7使用的是lock锁。

Redis满了,内存淘汰策略,LRU,LFU,ttl,随机,报错

        生产环境下多线程:结合线程池的配置,线程池的名称。http请求相应,是同步的。      

        生产环境中,运行一段时间总是宕机。攻击,堆内存溢出了可能,内存无法回收,可能会泄露。设定阈值,CPU达到70,报警,GC一直无法回收。

面试的:hashmap集合的原理。http无状态协议。java面试宝典。

        为什么不用jvm内置缓存,用Redis缓存?如果都在jvm一级缓存,容易爆掉。缓存就放缓存。Redis是二级缓存。

接口响应慢:网络,服务器,代码,SQL(实际考虑后两个)

        先分析下SQL有没有慢查询,有的话,分析下索引是否失效,是不是走了全部扫描。

为了避免接口响应慢,最好做成mq异步方式。CPU飙高的问题的话,线程不够,响应慢。

mq异步消费,不能及时获取返回结果。

        Mybatis拦截器做分页插件。Mybatis拦截器改写SQL语句,加limit做分页。

项目难题:

项目中的Redis的数据结构:

        Redis使用的是k-v存储的数据结构,key是设备号,value是Redis的list数组,数组里面包含10个对象,对应10个数据包,每分钟一个。每个对象里包含了6种信息,空气温湿度,土壤温湿度,co2,ph。lpush入队,rpop出队,实现数据更新。

        项目前端是HTML,打的是war包,自己部署在Tomcat的web-app下面。默认80端口。

内存溢出:

        1. 内存中加载的数据量过于庞大,如一次从数据库取出过多数据; 

        2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;

        3. 代码中存在死循环或循环产生过多重复的对象实体;

        5. 启动参数内存值设定的过小

内存泄漏:不再使用的对象还有指向他们的引用,最终OOM异常。

TCP/IP协议

1、三次握手:

第一次:客户端--服务器--TCP报文。标志位:SYN=1,序列号seq = x(一般是1);客户端--SYN-sent状态

第二次:服务器收到报文,发送给客户端:标志位:SYN-ACK(表示收到了客户端报文并且seq有效),序列号:seq = y,确认号:ack = x+1(收到客户端的seq并加1表示确认),服务器---SYN-RECV状态

第三次:客户端收到报文,发一段TCP报文,进入establish状态。报文标志位ACK,序列号seq = x+1,确认号:ACK = y+1,客户端收到后,进入到establish状态。握手完成。

2、四次挥手:

第一次:客户端---服务器---报文:标志位:FIN=1,序列号seq = U;客户端进入到FIN-wait-1阶段,半关闭状态,停止数据发送,但是可以接收数据。

第二次:服务器收到报文,结束establish阶段,发送报文给客户端,标志位ACK,序列号:seq = V,确认号,ack=U+1,服务器进入close-wait阶段,半关闭状态,可以发TCP报文。客户端收到TCP报文结束FIN-wait-1阶段,进入到FIN-wait-2阶段。

第三次:服务器发出ACK确认报文后,等待close-wait阶段,做好释放的准备,再次向客户端发送TCP报文。标志位FIN-ACK,序列号seq = W,确认号ack = U+1,服务器结束close-wait阶段,进入last-ack阶段,服务器停止数据发送,但仍能接受数据。

第四次:客户端收到TCP报文。结束FIN-wait-2阶段,进入到TIME-WAIT阶段,发送报文给服务器端,标志位ACK,序列号:seq = U+1,确认号ack = W+1,然后客户端等待2MSL时间,进入到closed阶段。服务器接收到报文后,进入到closed阶段。

为什么要三次握手:

        可靠的基于字节流的面向连接的传输层协议。网络不好也能保证传到对面,是字节流的方式传输的。三次握手,就是通信双方一共要发送三次请求。

        三次握手,有三个原因:

        1、通信双方必须要维护一个序列号,标记发送的数据包。确保被收到,双方要有确认的操作。

        2、TCP协议意味着要在不全的网络环境下建立安全的数据传输通道。保证安全性,可以4,5,6次握手,但没必要浪费资源。三次可以保证通道最小值建立安全连接。

        3、如果是两次链接,在网络环境较差的情况下,不知道请求是有效的还是无效的。(只能保证单向传输是畅通的)

  • 如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认

        握手三次,挥手需要四次的原因:是TCP半关闭造成的,停止发送,但是可以接受数据。TCP不允许半打开状态就单向传输数据,因此需要三次握手才可以。TCP半关闭状态是允许单向传输数据,服务器仍然可以像客户端发消息,这是TCP的双向通道互相独立导致,因此挥手需要4次。

        2MSL的意义:

        保证客户端发送的ACK可以到达服务器端,帮助其正常关闭。如果ACK丢失,会触发重传,这样的话 还可以保证FIN字段重新传到服务端,并重置这2MSL。客户端如果不等待2MSL就close了,那么服务器会一直在last-ACK状态。当客户端重新建立连接时,会直接终止连接。

        第二个意义:保证本次传输的报文信息从网络中消失。下次链接不会出现旧的报文请求。

3、输入URL,回车,发生了什么?

1、URL解析,域名解析,TCP连接,HTTP请求,响应请求,页面渲染。6个步骤

URL解析:判断输入内容是url还是待搜索的关键字,作出响应。根据语法规则和请求参数。

域名解析(找服务器ip地址):首先找浏览器的DNS缓存,没有再找操作系统的DNS缓存,在没有就找本地域名服务器,本地域名服务器用递归的方式进行查询,返回结果,本地域名服务器没有的话继续向上级域名服务器迭代查询。最后本地域名将ip传给操作系统,操作系统给浏览器,将ip缓存起来。

http请求:请求行(请求方法,url,协议版本),请求头,空行,消息体。

响应请求:状态行,响应头,响应正文。

页面渲染:html解析器解析DOM树,CSS解析成CSS模型,二者结合,重排,重绘。 

http响应码:HTTP 响应码大全_一起努力啊啊啊啊的博客-CSDN博客_http响应码

Spring

1、IOC/AOP 理论推导:

        IOC :以前创建对象的控制权在程序员,是new,当用户需求变动的时候,我们可能要去修改程序源码,代价是昂贵的,而且容易出错。我们使用set注入后,程序不在具有创建对象的主动性,变成了被动接受对象,本质上解决了这个问题,程序员不用再管理对象的创建了。所以IOC的本质就是获得依赖对象的方式反转了,我们把设计好的对象交给IOC容器,用到的时候直接去取。在Spring中实现控制反转的是IOC容器,实现的方式是依赖注入。

        DI表示依赖注入,对于IOC容器中的bean,如果bean之间有依赖的,IOC自动实现依赖对象的实例注入,接口注入、setter注入,构造器注入。

@Autowired @Resource

注解来源,注解用法:

来源:@Autowired注解是Spring提供的,Resource注解是JDK提供的。

用法:Autowired只能通过类型注入 ,Resource类型和名称注入都可以。

        @Autowired按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。

        @Resource装配顺序 1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常 2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常 3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常 4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;

依赖注入:使用set方式实现注入的。

普通注入(bean的value),set注入,其他注入。

bean Scopes作用域,默认singleton单例模式,也有原型模式

装配,byName,byType。jdk1.5支持注解,spring2.5支持注解。Spring4后注解开发必须导入AOP包。

Autowired可以不编写set方法,前提是自动装配的属性在IOC容器中存在,且符合名字byType

@Resource默认byname,也可以byType,使用名字的话根据bean的名字依赖注入byType根据类型。@Autowired是byname,require属性默认是True,匹配属性,不匹配就会报错。

2、AOP :把那些和业务无关的,却被业务代码所共同调用的逻辑或者责任封装起来,便于减少系统的复用代码,降低模块间的耦合程度;举例说明:(日志,权限)100个类,加日志,使用AOP进行切入。配置好AOP的切面。

AOP的实现:是基于动态代理实现的,如果代理的对象实现了某个接口,那么SpringAOP就会用jdk代理实现,对于没有实现接口的对象,无法使用jdk代理,转而使用cglib动态代理生成一个被代理的对象的子类来作为代理。

动态代理

实现的原理是反射。动态代理的代理类是动态生成的,不是直接写的。

proxy生成动态代理类实例,InvocationHandler调用处理程序,并返回结果。

3、4大元注解:

@Target 表示注解用在哪些地方

@Retention 表示注解用在什么地方还有效

@Document 将注解生成在java的Doc中

@Inherited 子类可以继承父类的注解

4、Spring自动装配机制的原理(约定大于配置)  springboot启动流程

        在SpringBoot启动类上添加@SpringBootApplication实现自动装配。自动将第三方bean装配到IOC容器中,不用再去写Bean的相关配置。

        SpringBootApplication是复合注解(SpringBootConfiguration,EnableAutoConfiguration,ComponentScan),真正实现自动装配的注解是EnableAutoConfiguration。三个核心技术:

1、引入starter启动依赖组件的时候,必须要包含Configuration配置类。在这个配置类里面我们要通过bean这个注解去声明装配到IOC容器中的bean对象

2、这个配置类放在第三方的jar包里面,基于约定大于配置的理念,放在指定的路径下。

3、springboot拿到第三方jar包里面声明的配置类以后,通过Spring提供的importSelector接口实现对这些配置类的动态加载。从而完成自动专配的动作。

简述SpringBoot的约定大于配置?4个方面。约定由于配置,是软件设计的范式,核心思想是减少开发人员对配置项的维护。Spring快速开发Spring的程序。节省了很多了Spring的配置项:管理jar包;维护web.xml,Dispatch-servlet.xml,应用部署到web服务器,第三方组件集成到Spring IOC。

还有:starter启动依赖,管理所有jar包版本。当前应用依赖了web的jar包,SpringBoot会自动内置tomcat,运行web应用。spring默认加载resource下面的application.property文件

5、消息通知。多用户

6、SpringCloud,Spring官方提出来的微服务解决方案,对微服务里面的各种应用场景定义的一套标准规范。Zuul实现应用网关,Eureka实现服务注册与发现,Ribbon实现负载均衡,Hystrix实现服务熔断。可以使用这些组件快速落地微服务项目,但是后期闭源,spring官方也开发了一些组件。Alibaba也是,忘了。。

7、什么是SPI?数据库驱动实现。

9、序列化和反序列化的理解。

10、kafka如何避免重复消费。1、kafka是通过offset来标记消费的。默认情况下,消费完成后会自动提交offset,避免重复消费

11、Spring里面两个相同的bean会报错吗?同一个xml里面不会有两个相同的bean,否则启动会报错,因为id是bean的唯一属性。两个不同配置文件里面,可以有相同id的bean,IOC加载的时候,默认把多个相同名字的bean覆盖。spring3.x时代,使用@configuration注解声明配置类,使用bean的注解实现bean的声明。这样的话,多个相同名字的bean,只会注册第一个,后面重复不注册了。

项目

黑客获取用户请求,模拟token如何防御:

1、可以绑定设备信息。如果黑客模拟了设备信息,防御是比较困难的。我们可以减少token的时间,过一会就重新登陆。

2、在重要的功能操作核心命令时,加入人脸识别,短信验证等,保证是本人在操作。

3、项目难点,业务难点,性能优化,数据库优化,

JVM内存模型:

1、栈、本地方法栈、程序计数器这三个部分都是线程独占的。

        栈也叫方法栈,是线程私有的,线程在执行每个方法时都会同时创建一个栈帧,用来存储局部变量表、操作栈、动态链接、方法出口等信息。调用方法时执行入栈,方法返回时执行出栈。

        java栈和本地方法栈很相似,java栈是为执行java方法服务的,本地方法栈是为了执行本地方法服务的。

        每条线程都有一个独立的的程序计数器,各线程间的计数器互不影响,因此该区域是线程私有的。不会 OOM

2、堆和方法区是线程共享的。

        堆是JVM管理的内存中最大的一块,jvm只有一个堆,目的是为了存放对象实例,几乎所有的对象实例都在这里分配。垃圾回收也在堆里面进行。当堆内存没有可用的空间时,会抛出OOM异常。

        堆被划分为:新生代和老年代。新生代被化为三个区:Eden区,From区,To区。

        新生代三分之一,老年代三分之二。Eden占新生代十分之八,另外的from和to区占十分之一

        在方法区(又被称为“永久代”,),存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

        新生代:保存新出现的对象,所以每次垃圾收集时都发现大批对象死去,只有少量对象存活,使用的是标记复制算法,复制量较少。

        老年代:存活比较久的,存活率高,使用标记清除算法

        永久代:永久代就是jvm的方法区,存放着被java虚拟机加载的类信息,静态变量,常量等数据。

        为什么要有两个Survivor区。如果没有的话,每次存活的对象都被送到老年代,堆里面的老年代很快就被填满了。填满触发FullGC,FullGC则更慢,因此要划分两个区域。

Minor GC ,Full GC 触发条件

Minor GC触发条件:当Eden区满时,触发Minor GC。

Full GC触发条件:

(1)调用System.gc时,系统建议执行Full GC,但是不必然执行

(2)老年代空间不足

(3)方法区空间不足

        老年代使用FullGC。当老年代容量满时,会触发一次Major GC,回收老年代和年轻代中不再被使用的对象资源。

        永久代(方法区)的垃圾回收有两部分内容:废弃常量和无用的类

        标记-清除算法收集垃圾的时候会产生许多的内存碎片 ( 即不连续的内存空间 ),此后需要为较大的对象分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次 GC 的收集动作。

类加载机制:加载、验证、准备、解析、初始化;

        一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载、验证、准备、解析、初始化、使用、卸载七个阶段,其中验证、准备、解析三个部分统称为连接,而前五个阶段则是类加载的完整过程 。

类加载器和双亲委派机制

        JVM中存在三个默认的类加载器,BootstripClassload、ExtClassload、AppClassload;双亲委派就是JVM在加载类时,会委派给父类extclassload和父类的父类bootstripclassload的loadclass方法,没加载到才自己加载。

select poll epoll 介绍一下:

        提供一种IO复用的方式。即让单个进程可以监视多个文件描述符,一旦某个fd就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作。

        select基于long类型数组,select是基于遍历来查找fd事件的,所以时间复杂度为O(n)。select在监听过程中,每次需要把fd列表从用户态拷贝到内核态,然后再遍历所有fd,判断有无读写事件发生。这也导致select在监听IO数量较多的情况下,性能开销极大(poll也有这个缺点)

        为了减少数据拷贝带来的性能损坏,所以内核对单个进程可监视的fd数量做了限制。

        水平触发:如果用户程序没有处理select所报告的fd,则下一次select时会继续报告此fd。

poll和select 差不多,区别是poll是基于链表的,实践复杂度也是On

        epoll:epoll底层是基于哈希表和回调函数的,所以时间复杂度为O(1)。

epoll有两种模式,LT和ET,LT是默认的模式,ET是高速模式。

  • LT模式(水平触发):epoll_wait检测到某fd事件就绪并通知用户程序时,如果用户程序不处理该事件,则每次epoll_wait都会返回这个fd事件,不断提醒用户程序去操作。

  • ET模式(边缘触发):当一个fd里有新的数据变化时,epoll只会向用户程序返回一次报告,直到下次再有新的数据流入之后,才会再次返回报告,无论fd中是否还有数据可读。

优点

不会像select或poll那样因为打开的fd过多而影响性能。
没有最大并发限制连接限制。
epoll在监听到fd变化后不必像select或poll那样返回整个fd列表来进行遍历查找,而是只将产生变化的fd(即活跃的fd)放入一个列表中,调用callback函数返回。
使用了mmap技术,利用mmap()文件映射内存加速与内核空间的消息传递。

缺点:

  • 当活跃连接数过多时可能会有性能问题。
  • epoll机制需要很多回调函数,在连接数较少的情况下,性能可能不如select和poll。

计算机网络

1、Https和http协议

1、端口http80,https443        2、安全系数,明文,ssl/tls加密        3、连接方式无状态连接,同一客户端发送请求,在服务器端是没有上下文关系的。        4、证书        5、消耗资源

2、TCP和UDP区别

tcp和udp有什么区别-常见问题-PHP中文网

1、面向连接,无连接        2、可靠性        3、有序性        4、数据边界        5、速度

6、发送消耗        7、报头大小        8、拥塞或流量控制        9、应用        10、上层协议

TCP和UDP是传输层协议。

3、TCP拥塞控制机制

TCP/IP路由是哪一层实现

算法

1、异或运算(无进位相加)

2、排序:插排,冒泡,归并,快排,希尔,堆(完全二叉树)。(算法原理,时间复杂度,空间复杂度,稳定性)稳定性:冒泡,插排,归并,桶。

3、判断有环无环。快慢指针,快走2,慢走1,相遇后快指针到起始位置,快慢都走1,再次相遇在环的入口。

4、二叉树的递归序:前序中序后序遍历。层序遍历用队列。非递归实现树的前中后序遍历。前序:每次弹出一个节点,弹出就打印,现右后左。中序:左右头,每次左树进栈,对弹出节点的右树重复。后序,准备两个栈,弹出一个放入s2,先左在右,弹出就放入S2,然后S2依次弹出,就是后序遍历顺序。原理就是:所有的树都可以被左树分解掉。

5、图,广度优先遍历和深度优先遍历。

6、拓扑排序:import的时候就是拓扑排序,不能循环编译。先找入度为0的点。

7、kruscal和prim算法,无向图最小生成树算法。

8、Djstralar算法,单源点最短路径。

9、前缀树

10、N皇后,汉诺塔问题

11、哈夫曼树

12、贪心策略

13、打印字符串全部子序列

八股文

操作系统部分:        

        进程、线程、协程的区别。并发和并行有啥区别,进程和线程的切换流程。虚拟地址空间切换为啥耗时,进程间通信方式,线程同步方式。线程分类。同一进程的线程有啥区别。主线程消亡,子线程如何。什么是临界区,如何解决冲突。死锁。进程调度策略有哪几种,进程的状态。什么是分页,什么是分段,有啥区别,什么是虚拟内存,逻辑地址和物理地址的转换过程,局部性原理,交换空间,页面置换算法。缓冲区溢出,有啥危害,僵尸进城是什么,怎么解决。孤儿进程是什么有什么危害。io多路复用,select poll epoll 的区别,都讲讲。硬连接和软连接有啥区别,终端的处理过程,终端盒轮训,为啥区分内核态用户态,零拷贝,磁盘调度算法,操作系统如何使用锁,堆栈区别,CPU使用率和cpu负载有什么区别,什么是大端,什么是小端。

        QQ.exe点击后load到内存里。

1、协程就是微线程,存在线程中,是比线程更小的运行单元,本质上是一个特殊的函数,一个协程通常只有几KB,高并发编程的时候,创建几万个甚至几十万个协程对内存毫无压力,协程是用户级的,不是系统级的,所以协程的创建、销毁、切换全部是用户态完成的,性能非常高。

比喻:计算机是一家公司,操作系统是管理层,进程是各个项目组,线程项目组的各个员工,协程是员工手里的任务清单。

2、并发和并行

        并发是指一个处理器同时处理多个任务。
        并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。
        并发是逻辑上的同时发生(simultaneous),而并行是物理上的同时发生。
        来个比喻:并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。

同一个进程中的多个线程之间可以并发执行。

3、进程和线程的切换

        进程切换涉及虚拟地址空间的切换而线程不会。因为每个进程都有自己的虚拟地址空间,而线程是共享所在进程的虚拟地址空间的,因此同一个进程中的线程进行线程切换时不涉及虚拟地址空间的转换。

4、为啥虚拟地址切换耗时。

        进程都有自己的虚拟地址空间,把虚拟地址转换为物理地址需要查找页表,页表查找是一个很慢的过程,因此通常使用Cache来缓存常用的地址映射,这样可以加速页表查找,这个cache就是TLB。由于每个进程都有自己的虚拟地址空间,那么显然每个进程都有自己的页表,那么当进程切换后页表也要进行切换,页表切换后TLB就失效了,cache失效导致命中率降低,那么虚拟地址转换为物理地址就会变慢,表现出来的就是程序运行会变慢,而线程切换则不会导致TLB失效,因为线程线程无需切换地址空间,因此线程切换快。

5、进程间通信方式

        一般来说是共享存储,消息传递,管道通信这三种方式。

        消息传递是基于操作系统的原语进行通信的,进程间可以通过send和receive直接进行通信。

        管道通信,管道就是一块固定大小的缓冲区,这个管道是单项通信的文件,一个进程只能写,另一个进程只能读,如果要全双工通信的话,需要2个管道。

        共享存储是性能最好的,分为共享数据结构,共享存储区,共享数据结构是操作系统内核提供的,操作时空间有限,并且会产生用户区和内核其余数据拷贝的问题,还有上下文切换的问题,性能一般。共享存储区使用的更多,是在用户空间实现的,不涉及内核态切换,一般是在物理内存上开辟一个空间,操作系统将这个空间分配给多个进程,这样就可以实现进程之间的通信和数据交换。

        管道、命名管道、信号、消息队列、共享内存、内存映射、信号量、Socket 。

虚拟内存

        虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),虚拟存储器

JUC:

        原子类、锁、线程池、并发容器、同步工具

说说线程同步方式:Synchronize 和 Lock

        Synchronize :同步普通方法(this当前类)、 同步静态方法(当前类的class对象)、同步代码块。

        Lock实现类都是基于AQS实现的。AQS是多线程同步器,提供了两种锁的机制,分别是排他锁和共享锁。比如Lock中的ReentrantLock重入锁,他的实现就是用到了AQS中的一个排他锁的功能CountDownLatch以及Semaphore,都用到了AQS中的共享锁的功能。

synchronize和lock的区别:

特性区别:关键字,juc包下的接口,有实现类reentrantlock等,

用法区别:对象,方法,代码块;lock和unlock来实现;lock灵活,避免死锁,unlock写在finally。synchronize执行完或者异常才释放,是被动释放的。

性能区别:性能差别不大,实现上有点区别。悲观锁;jdk6以后实现了锁升级,偏轻重,锁升级机制来优化。lock用自旋锁进行了优化。

用途区别:非常复杂情况下建议使用lock,因为synchronize只提供了非公平锁,lock提供了公平锁和非公平锁。

        volatile关键字

        ReentrantLock类是可重入、互斥、实现了JUC里面的Lock接口。

ThreadLocal

        使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,
副本之间相互独立,就不需要对变量进行同步,但注意的是,ThreadLocal用完后需要手动remove,否则内存泄漏。因为线程执行完不会被销毁,而是到线程池等待。

SpringBoot常用注解:(约定大约配置)

 @SpringBootApplication:Spring Boot项目的核心注解,用于开启自动配置

@EnableAutoConfiguration注解:启动Spring应用程序上下文时进行自动配置,它会尝试猜测并配置项目可能需要的Bean。

@Import注解:

@Conditional注解:可根据是否满足指定的条件来决定是否进行Bean的实例化及装配

java线程分类:

用户线程,守护线程

守护线程(后台线程)守护线程是为用户线程服务的线程,当所有的用户线程执行结束之后,守护线程也会随之结束。JVM的垃圾收集器是典型的守护线程。操作日志,监控内存,垃圾回收。

main方法默认为用户线程。

jdk线程状态:

        new,Runnable,Blocked,Waiting,Time_waiting超时等待,等待时间达到阈值的状态,terminated死亡 一共6种。

6、主线程退出后,子线程如何?

        主线程退出后子线程的状态依赖于它所在的进程,如果进程没有退出的话子线程依然正常运转。如果进程退出了,那么它所有的线程都会退出,所以子线程也就退出了。

7、临界区

         临界资源是一次仅允许一个进程使用的共享资源。各进程采取互斥的方式,实现共享的资源称作临界资源。硬件有:打印机,磁带机等;软件有:消息队列,变量,数组,缓冲区等。诸进程间采取互斥方式,实现对这种资源的共享。

9、进程调度策略

  • 先来先服务
  • 短作业优先
  • 最短剩余时间优先
  • 时间片轮转:

        将所有就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。

        时间片轮转算法的效率和时间片的大小有很大关系:因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。 而如果时间片过长,那么实时性就不能得到保证

  • 优先级调度:为每个进程分配一个优先级,按优先级进行调度。为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级

什么是分页,什么是分段?

        用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应地,内存空间分成若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。为了内存中找到每个页面对应的物理块,系统为每个进程建立一张页表,用于记录进程逻辑页面与内存物理页面之间的对应关系。

分页和分段的区别:
共同点:两者都采用离散分配方式,且都地址映射机构来实现地址的转换    
不同点:
1:页是信息的物理单位采用分页存储管理方式是为了实现离散分配方法。提高内存的利用率,采用分段目的主要在于能更好的满足用户的需求
2:页的大小固定且有系统决定,在采用分页存储管理方式中直接由硬件实现。而段的大小不固定,决定于用户所编写的程序
 

缓冲区溢出

        缓冲区溢出是指当计算机向缓冲区填充数据时超出了缓冲区本身的容量,溢出的数据覆盖在合法数据上。利用缓冲区溢出攻击,可以导致程序运行失败、系统宕机、重新启动等后果。可以利用它执行非授权指令,甚至可以取得系统特权,进而进行各种非法操作。

僵尸进程

        子进程比父进程先结束,父进程没有回收子进程,释放资源,子进程变成一个僵尸进程。就是已经结束了的进程,但是没有从进程表中删除。太多了会导致进程表里面条目满了,进而导致系统崩溃,倒是不占用其他系统资源。top指令找到zombie进程,杀死其父进程。严格来讲,僵死进程并不是问题的根源,罪魁祸首是产生出大量僵死进程的那个父进程。

#僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

孤儿进程

        孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

页面置换算法

        理解页面置换算法:当发生缺页中断时,如果操作系统内存中没有空闲页面,则操作系统必须在内存选择一个页面将其移出内存,以便为即将调入的页面让出空间,而用来选择淘汰哪一页的规则叫做页面置换算法。最佳置换算法(OPT)每个页面都可以用在该页面首次被访问前所要执行的指令数进行标记。最佳页面置换算法只是简单地规定:标记最大的页应该被置换。这个算法唯一的一个问题就是它无法实现。当缺页发生时,操作系统无法知道各个页面下一次是在什么时候被访问。虽然这个算法不可能实现,但是最佳页面置换算法可以用于对可实现算法的性能进行衡量比较。先进先出置换算法(FIFO)即先进入内存的页,先退出内存

CPU使用率和cpu负载有什么区别

CPU利用率:显示的是程序在运行期间实时占用的CPU百分比

CPU负载:显示的是一段时间内正在使用和等待使用CPU的平均任务数。比如使用率50%,负载2,表示正在执行2个程序

局部性原理

        是指CPU访问存储器时,无论是存取指令还是存取数据,所访问的存储单元都趋于聚集在一个较小的连续区域中。

  1. 时间局部性:如果程序中的某条指令一旦执行,则不久之后该指令可能再次被执行;如果某数据被访问,则不久之后该数据可能再次被访问。
  2. 空间局部性:是指一旦程序访问了某个存储单元,则不久之后,其附近的存储单元也将被访问。

交换空间

        操作系统把物理内存(physical RAM)分成一块一块的小内存,每一块内存被称为页(page)。当内存资源不足时,Linux把某些页的内容转移至硬盘上的一块空间上,以释放内存空间。硬盘上的那块空间叫做交换空间(swap space),而这一过程被称为交换(swapping)。物理内存和交换空间的总容量就是虚拟内存的可用容量。

虚拟内存和物理内存

        虚拟内存是使得应用程序认为拥有连续的可用的内存,而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。物理内存是在计算机运行时为操作系统和各种程序提供临时储存。

        物理内存在没有虚拟内存概念的时候是物理寻址的,32位的话,每次寻址2**32次方就是4G,内存很快就消耗光了,而且其他进程可以修改当前进程的数据,不安全。

IO多路复用

        IO 是指网络 IO,多路指多个TCP连接(即 socket 或者 channel),复用指复用一个或几个线程。意思说一个或一组线程处理多个 TCP 连接。最大优势是减少系统开销小,不必创建过多的进程/线程,也不必维护这些进程/线程。IO 多路复用的三种实现方式:select、poll、epoll

select、poll、epoll机制

        客户端操作服务器时就会产生这三种文件描述符(简称fd):writefds(写)、readfds(读)、和 exceptfds(异常)。select 会阻塞住监视 3 类文件描述符,等有数据、可读、可写、出异常或超时就会返回;返回后通过遍历 fdset 整个数组来找到就绪的描述符 fd,然后进行对应的 IO 操作。

        优点:几乎在所有的平台上支持,跨平台支持性好。

        缺点: 由于是采用轮询方式全盘扫描,会随着文件描述符 FD 数量增多而性能下降。
        每次调用 select(),都需要把 fd 集合从用户态拷贝到内核态,并进行遍历(消息传递都是从内核到用户空间)。
        单个进程打开的 FD 是有限制(通过FD_SETSIZE设置)的,默认是 1024 个,可修改宏定义,但是效率仍然慢。

poll 几乎等同于select ,轮询+遍历。区别是没有最大文件描述符限制。

epoll效率高,只能在Linux下执行

硬链接和软连接:

       硬链接: 即一个inode节点对应两个不同的文件名,两个文件名指向同一个文件,A和B对文件系统来说是完全平等的。如果删除了其中一个,对另外一个没有影响。每增加一个文件名,inode节点上的链接数增加一,每删除一个对应的文件名,inode节点上的链接数减一,直到为0,inode节点和对应的数据块被回收。

        软连接比较常用,ln -s 源文件 目标文件,不同的目录,用到相同的文件时,用这个命令即可,不用重复占用磁盘资源。

零拷贝 :0是次数,拷贝是数据转移

        磁盘内容到远程服务器:磁盘--内核缓冲区---用户缓冲区---socket缓冲区---网卡缓冲区--目标服务器。4次拷贝,2次浪费。内核--用户空间,用户空间再次到内核空间,用户空间和内核空间切换会CPU上下文切换,影响性能。省去2次拷贝,从内核直接拷贝到socket,用DMA技术,减少上下文切换。java中底层实现sendfile方法,mmap文件映射机制。磁盘文件映射到内存,修改内存即可,省去了开销。

        大端存储是指低字节存储在高地址;
        小端存储是指低字节存储在低地址。

        锁机制,用于保证临界区代码的安全。

堆与栈的区别有:

1、栈由系统自动分配,而堆是人为申请开辟;2、栈获得的空间较小,而堆获得的空间较大;3、栈由系统自动分配,速度较快,而堆一般速度比较慢;4、栈是连续的空间,而堆是不连续的空间。(申请方式,大小,效率,空间)

为了保证系统的稳定性、安全性,需要在系统中划分内核态、用户态,本质意义是进行权限保护

 根据进程请求访问磁盘的先后顺序进行调度

优先处理的磁道是与当前磁头最近的磁道。可以保证每次寻道时间最短,但是不能保证总的寻道时间最短。(其实是贪心算法的思想,只是选择眼前最优,但是总体未必最优)。

红黑树,

二叉树,AVL树,红黑树区别

hashmap原理,线程不安全表现在哪里

tcp和UDP分别用在什么场景

Tcp延迟确认机制,为什么

TCP滑动窗口

TCP拥塞控制机制

tcp重传机制

URL到浏览器展示的过程讲一下

现在如果只有IP或者mac还可以完成通讯吗

https请求过程,

为什么要用对称加密,为什么对称加密比非对称快

虚拟内存说一下

分页和分段

jvm虚拟机了解多少

MySQL开启事务的完整过程

SQL优化

分库分表了解过吗

MySQL解决幻读

乐观锁和悲观锁的区别讲一下

数据库怎么设计的

防止curl命令直接调用接口

写一个LRU算法

多线程数一下

不安全问题说一下

什么是多线程上下文切换

threadloacal 了解吗,线程池的用法和优势

虚拟存储怎么实现

32为操作系统最大虚拟内存空间是多少

进程间通信方式

Linux了解吗

日志文件中,关键信息查询,日志文件比较大,怎么查询

SQL语句:班级号学生成绩,统计平均成绩

分布式概念

设计分布式id生成器怎么设计

算法题:链表对折,125面值的钱币,凑出n元的方案有多少种

数据库有做分库分表吗?怎么做的

Redis基本数据结构,list结构用过吗,底层如何实现的吗

MySQL索引有几种,Innodb是表锁还是行锁?

进程线程,协程的区别

互斥锁那几种模式,底层

Linux常用命令,poll用过

http依次连接读个请求,不用后端返回可以吗

tcp和udp区别,udp使用场景

timewait 和closewait的原因,timewait作用

5亿整数大文件,怎么排序

java调用start方法会调用run方法,为什么不直接调用run:

        JVM调用start方法会先创建一个新线程,新线程会调用执行run方法,这才会起到多线程的效果,如果直接用thread的run方法,run方法还是在主线程里面运行,线程是顺序执行的,达不到多线程的效果了

if 会影响CPU的执行效率?

        操作计算机的时候,会被拆分成指令,指令分为4部,取指,译码,执行,回写。现在CPU操作时指令流水线操作,支持我们刷视频玩游戏。但是if语句会被编译成一条跳转指令,跳转指令完成前,CPU不知道哪个指令进入流水线,所以cpu靠猜去执行,猜错了的话,CPU刚执行的猜错的操作全部作废,猜错了会有性能损耗,猜的过程叫分支预测。所以尽量让cpu猜对。

线程池的7个核心参数:

        ThreadPollExector 构造函数有7个参数:

        核心线程数、空闲线程存活时间,unit空闲线程存活时间单位(毫秒,秒等),最大线程数、工作队列、线程工厂、拒绝策略。

模拟面试:

MySQL索引那些时候会失效:

系统调用的过程发生了什么,内核态用户态转化又发生了什么

虚拟地址怎么转换的

http是用什么协议实现的

tcp用的是什么机制实现的可靠性

TCP粘包拆包问题怎么解决的,有什么好处坏处?

收到的数据长度不够怎么办

        螺旋矩阵

        岛屿数量

接口和抽象类的区别

重写equals还要重写hashcode

线程池拒绝策略有哪些

用了无界队列的话,有什么坏处呢

JVM的架构有什么

class文件的加载过程

双亲委派

new String(“aa”)创建了几个对象

final的原理

什么对象进入老年代

CMS和G1的区别

指导如何进行JVM调优吗

数据结构:hashmap实现原理,不安全表现哪里

优先队列的实现原理

大小根堆有实现过吗

手写一下归并排序

OS:

对上下文切换的理解

减少并发编程中的 上下文切换

CAS的原理是什么

synchronize的锁升级策略有了解吗

进程间通信方式

线程间通信方式

TCP滑动窗口原理

ping命令的底层是什么协议

Spring IOC AOP 

解决循环依赖

Spring事物原理,讲一下

隔离级别

MySQL使用的那个隔离级别,MySQL的幻读,怎么解决的

where a=x and b<x and c>x怎么建立索引?

token的组成讲一下

用Redis的原因,

统一缓存是怎么实现的

保证缓存一致性怎么保证

百万并发,现在应该怎么应对呢

技术难点怎么解决的,实际场景说一下

平时都是怎么学习的

介绍一下UDP

CPU的调度算法有哪些?

优先级调度算法的具体实现方式

如何控制多线程优先级的顺序呢?

进程的上下文包含哪些部分呢

进程切换和线程切换的区别

为什么线程切换的开销更小

select poll epoll

中断的概念讲一下

用户态和内核态的区别是什么,哪些系统调用会从用户态到内核态,什么时候会显示切换状态

MySQL索引

如何避免回表

说说MySQL一次查询的过程

MySQL事物和隔离级别讲一下把

redolog的两阶段提交过程能不能说一下

设计模式,

编程题:

合并两个排序数组

数组最大子序列和

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值