1.List,Set,Map三者的区别
- List(对付顺序的好帮手): List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象
- Set(注重独一无二的性质): 不允许重复的集合。不会有多个元素引用相同的对象。
- Map(用Key来搜索的专家): 使用键值对存储。Map会维护与Key有关联的值。两个Key可以引用相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。
2.Arraylist 与 LinkedList 区别
-
是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
-
底层数据结构: Arraylist 底层使用的是Object数组;LinkedList 底层使用的是双向链表 数据结构(JDK1.6之前为循环链表,JDK1.7取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!)
-
插入和删除是否受元素位置的影响:
-
ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e) 方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element) )时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。
-
LinkedList 采用链表存储,所以对于add(E e)方法的插入,删除元素时间复杂度不受元素位置的影响,近似 O(1),如果是要在指定位置i插入和删除元素的话((add(int index, E element)) 时间复杂度近似为o(n))因为需要先移动到指定位置再插入。
-
是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index) 方法)。
-
内存空间占用: ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。
3.BIO和NIO的区别,写文件时如何判断缓冲区buffer已经满了,linux的五种IO模型。
- 1)阻塞I/O(blocking I/O)
- 2)非阻塞I/O (nonblocking I/O)
- 3)I/O复用(select 和poll) (I/O multiplexing)
- 4)信号驱动I/O (signal driven I/O (SIGIO))
- 5)异步I/O (asynchronous I/O (the POSIX aio_functions))
NIO与BIO的区别
- BIO流是阻塞的,NIO流是不阻塞的。
- BIO 面向流(Stream oriented),而 NIO 面向缓冲区(Buffer oriented)。
- NIO 通过Channel(通道)进行读写。通道是双向的,可读也可写,而流的读写是单向的。无论读写,通道只能和Buffer交互。因为 Buffer,通道可以异步地读写。
- NIO有选择器,而BIO没有。选择器用于使用单个线程处理多个通道。因此,它需要较少的线程来处理这些通道。线程之间的切换对于操作系统来说是昂贵的。 因此,为了提高系统效率选择器是有用的。
BIO:进程会一直阻塞,直到数据拷贝完成 应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。 如果数据没有准备好,一直等待….数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。
NIO:非阻塞IO通过进程反复调用IO函数(多次系统调用,并马上返回);在数据拷贝的过程中,进程是阻塞的; 把SOCKET设置为非阻塞模式,即通知系统内核:在调用Windows Sockets API时,不要让线程睡眠,而应该让函数立即返回。在返回时,该函数返回一个错误代码。图所示,一个非阻塞模式套接字多次调用recv()函数的过程。前三次调用recv()函数时,内核数据还没有准备好。因此,该函数立即返回WSAEWOULDBLOCK错误代码。第四次调用recv()函数时,数据已经准备好,被复制到应用程序的缓冲区中,recv()函数返回成功指示,应用程序开始处理数据。
-
传统的BIO里面socket.read(),如果TCP RecvBuffer里没有数据,函数会一直阻塞,直到收到数据,返回读到的数据。
-
对于NIO,如果TCP RecvBuffer有数据,就把数据从网卡读到内存,并且返回给用户;反之则直接返回0,永远不会阻塞。
-
最新的AIO(Async I/O)里面会更进一步:不但等待就绪是非阻塞的,就连数据从网卡到内存的过程也是异步的。
-
换句话说,BIO里用户最关心“我要读”,NIO里用户最关心"我可以读了",在AIO模型里用户更需要关注的是“读完了”。
-
NIO一个重要的特点是:socket主要的读、写、注册和接收函数,在等待就绪阶段都是非阻塞的,真正的I/O操作是同步阻塞的(消耗CPU但性能非常高)。
4.死锁是什么,怎么实现死锁
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
死锁的四个必要条件
- 互斥条件
- 请求和保持条件
- 不剥夺条件
- 环路等待条件
实现死锁
public class DeadLockDemo {
//两个锁对象
private static Object obj1 = new Object();
private static Object obj2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
while (true) {
synchronized (obj1) {
System.out.println(Thread.currentThread().getName() + "获得obj1锁");
try {
//睡3秒让另一个线程先获得obj2锁
TimeUnit.SECONDS.sleep(3);
//此时另一个线程已经持有obj2锁,这里请求获取obj2锁就会发生死锁
synchronized (obj2) {
System.out.println(Thread.currentThread().getName() + "获得obj2锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(() -> {
while (true) {
synchronized (obj2) {
System.out.println(Thread.currentThread().getName() + "获得obj2锁");
try {
//睡3秒让另一个线程先获得obj1锁
TimeUnit.SECONDS.sleep(3);
synchronized (obj1) {
System.out.println(Thread.currentThread().getName() + "获得obj1锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
5.SpringMVC流程
- 用户发送请求
- 请求交由核心控制器处理
- 核心控制器找到映射器,映射器看看请求路径是什么
- 核心控制器再找到适配器,看看有哪些类实现了Controller接口或者对应的bean 对象
- 将带过来的数据进行转换,格式化等等操作
- 找到我们的控制器Action,处理完业务之后返回一个ModelAndView对象
- 最后通过视图解析器来对ModelAndView进行解析
- 跳转到对应的JSP/html页面
6.服务器(客户端)端大量端口处于timewait阶段
7.Mysql的事务是什么,有哪几种隔离级别?
-
事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。
-
事务的ACID
-
1 、原子性。事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做
-
2 、一致性。事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。
-
3 、隔离性。一个事务的执行不能被其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
-
4 、持续性。也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
事务有四种隔离级别
- Read Uncommitted(读取未提交内容)。 读取未提交的数据,也被称之为脏读(Dirty Read)。
- Read Committed(读取提交内容) 一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。
- Repeatable Read(可重读)这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
- Serializable(可串行化) 这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:
脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
8.Mysql的索引是什么?有什么作用,为什么要使用B+Tree?
索引(Index)是帮助MySQL高效获取数据的数据结构。提取句子主干,就可以得到索引的本质:索引是数据结构。有普通索引、UNIQUE索引、主键索引
- B+Tree 只在叶子节点存储数据 ,在非叶子结点只存储索引。先定位索引再通过索引高效快速定位数据。
- 所有的叶子结点之间都有链指针,提高了Mysql顺序查找的能力
- Mysql设计利用了磁盘预读原理,将一个B+Tree节点大小设为一个页大小(4KB的整数倍),在新建节点时直接申请一个页的空间,这样就能保证一个节点物理上存储在一个页里,加之计算机存储分配都是按页对齐的,这样就实现了每个Node节点只需要一次I/O操作。
- B+Tree 单个节点能存放多个子节点,相同IO次数,检索出更多信息
9.数据库的索引种类(常见四种)有哪些?什么是慢查询?
- 普通索引、Unique索引(唯一索引)、主键索引、组合索引
- 慢查询是指超过指定时间( long_query_time)的SQL语句查询
- set global slow_query_log = 1;开启慢查询日志。
- set global long_query_time=0;调整慢查询阈值时间。
6.几千万的数据要存入数据库怎么优化?
-
- 一条SQL语句插入多条数据。
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)
VALUES ('0', 'userid_0', 'content_0', 0), ('1', 'userid_1', 'content_1', 1);
-
- 在事务中进行插入处理。
START TRANSACTION;
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)
VALUES ('0', 'userid_0', 'content_0', 0);
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)
VALUES ('1', 'userid_1', 'content_1', 1);
...
COMMIT;
-
- 数据有序插入。 数据有序的插入是指插入记录在主键上是有序排列,例如datetime是记录的主键:
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)
VALUES ('0', 'userid_0', 'content_0', 0);
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)
VALUES ('1', 'userid_1', 'content_1', 1);
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)
VALUES ('2', 'userid_2', 'content_2',2);
10.Linux怎么查看端口号?常用的Linux命令。
netstat -atulnp
- -a :all,表示列出所有的连接,服务监听,Socket资料
- -t :tcp,列出tcp协议的服务
- -u :udp,列出udp协议的服务
- -n :port number, 用端口号来显示
- -l :listening,列出当前监听服务
如何查看端口号被占用
-
1、lsof -i:端口号(list open files)
-
2、netstat -tunlp|grep 端口号
11.UDP如何保证可靠传输?(应用层上面实现)
- 1、添加seq/ack机制,确保数据发送到对端
- 2、添加发送和接收缓冲区,主要是用户超时重传。
- 3、添加超时重传机制。
12.什么是一致性hash算法?(目的是解决分布式缓存)
- 一致性哈希算法将整个哈希值空间映射成一个虚拟的圆环,整个哈希空间的取值范围为0~2^32-1。整个空间按顺时针方向组织。0~2^32-1在零点中方向重合。接下来使用如下算法对服务请求进行映射,将服务请求使用哈希算法算出对应的hash值,然后根据hash值的位置沿圆环顺时针查找,第一台遇到的服务器就是所对应的处理请求服务器。
- 当增加一台新的服务器,受影响的数据仅仅是新添加的服务器到其环空间中前一台的服务器(也就是顺着逆时针方向遇到的第一台服务器)之间的数据,其他都不会受到影响。综上所述,一致性哈希算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性
6.Redis和Mysql的区别?
- 类型:redis是一个key-value存储系统,是nosql,即非关系型数据库,和memcached都是缓存数据库.mysql是关系型数据库
- 存储:redis用于存储使用相对频繁的数据到内存中,mysql用于存放持久化数据到磁盘中
- 速度:redis读取速度快,mysql相对速度较慢
- 数据类型:redis数据类型:字符串类型(string),散列类型(hash),列表类型(list),集合类型(set),有序集合类型(zset)。mysql数据类型,大致三类:数值,日期,字符。
一般来说**,mysql用于写入和更新**,redis用于读取。这样就说mysql->redis的同步用的比较多。 mysql作为数据持久化和管理比redis好太多,redis大多只用来做 数据读取缓存、队列、锁、等等的使用。因为需求的不同,要根据具体业务场景去选型,一般都是配合使用。
13.将栈中元素排序,只允许使用一个额外的辅助栈。
public static void sortStack(Stack<Integer> stack) {
Stack<Integer> helperStack = new Stack<>();
while (!stack.isEmpty()) {
//先取出当前栈顶元素暂存
int temp = stack.pop();
//如果辅助栈不为空,且当前元素小于辅助栈顶元素,那么需要将辅助栈顶元素压回主栈
while (!helperStack.isEmpty() && temp < helperStack.peek()) {
stack.push(helperStack.pop());
}
//将当前元素压入辅助栈
helperStack.push(temp);
}
//将逆序的辅助栈依次压回主栈
while (!helperStack.isEmpty()) {
stack.push(helperStack.pop());
}
}
14.Redis消息队列
- 基于List的 LPUSH+BRPOP 的实现
- 使用rpush和lpush操作入队列,lpop和rpop操作出队列。 List支持多个生产者和消费者并发进出消息,每个消费者拿到都是不同的列表元素。但是当队列为空时,lpop和rpop会一直空轮训,消耗资源;所以引入阻塞读blpop和brpop(b代表blocking),阻塞读在队列没有数据的时候进入休眠状态。x需要解决空闲连接的问题,做消费者确认ACK麻烦。
- PUB/SUB,订阅/发布模式
- SUBSCRIBE,用于订阅信道PUBLISH,向信道发送消息 UNSUBSCRIBE,取消订阅此模式允许生产者只生产一次消息,由中间件负责将消息复制到多个消息队列,每个消息队列由对应的消费组消费。
- 缺点:消息一旦发布,不能接收。换句话就是发布时若客户端不在线,则消息丢失,不能寻回不能保证每个消费者接收的时间是一致的 若消费者客户端出现消息积压,到一定程度,会被强制断开,导致消息意外丢失。通常发生在消息的生产远大于消费速度时 可见,Pub/Sub 模式不适合做消息存储,消息积压类的业务,而是擅长处理广播,即时通讯,即时反馈的业务。
- 基于Sorted-Set的实现
- Sortes Set(有序列表),类似于java的SortedSet和HashMap的结合体,一方面她是一个set,保证内部value的唯一性,另一方面它可以给每个value赋予一个score,代表这个value的排序权重。内部实现是“跳跃表”。 有序集合的方案是在自己确定消息顺ID时比较常用,使用集合成员的Score来作为消息ID,保证顺序,还可以保证消息ID的单调递增。通常可以使用时间戳+序号的方案。确保了消息ID的单调递增,利用SortedSet的依据。Score排序的特征,就可以制作一个有序的消息队列了。
- 基于Stream类型的实现
15.Mysql中explain有哪些字段,type有哪些类型。
explain字段:
id、select_type 、table 、type 、possible_keys 、key 、key_len 、ref 、rows、Extra
type类型:ALL、index、range...null
16.Tomcat线程池数量大小,几万请求Tomcat撑得住吗?线程池应该怎么设置。
Tomcat的默认最大线程数量是200个。(maxThreads)
17.Http1.0和2.0的区别。
- 1.新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
- 2.多路复用(MultiPlexing),即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。
- 3.header压缩,如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
- 4.服务端推送(server push),同SPDY一样,HTTP2.0也具有server push功能。
18.多路IO复用的原理。
- 使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处理多个IO请求的目的。
19.JVM可以代码调用GC吗?调用后会马上执行吗?
可以,只是通知JVM不会立即执行。 System.gc()、Runtime.getRuntime().gc(); 另Java的GC是由JVM自行调动的,在需要的时候才执行,上面的指令只是告诉JVM尽快GC一次,但不会立即执行GC。
20.聚簇索引和自己创建的索引有什么区别?
聚簇索引:将数据存储与索引放到了一块,索引结构的叶子节点就是数据结点。
非聚簇索引:将数据与索引分开存储,索引结构的叶子节点仍然是索引结点并指向了数据对应的位置。
- 聚簇索引主键的插入速度要比非聚簇索引主键的插入速度慢很多。如果碰到不规则数据插入时,造成频繁的页分裂。
- 聚簇索引适合排序,非聚簇索引不道适合用在排序的场合。因为聚簇索引本身已经是按照物理顺序放置的,排序很快。非聚簇索引则没有按序存放版,需要额外消耗资源来排序。
- 聚簇索引的主键值,应尽量是连续增长的值,而不是要是随机值(UUID)
21.TCP三次握手的第三次握手发送ACK能携带数据吗?第二次握手可以携带吗?
第三次客户端已经处于established状态,ACK可以携带数据。 第二次握手和第一次握手都不可以携带数据,因为SYN包中不能携带数据。
假如第一次握手可以携带数据的话,那对于服务器是不是太危险了,有人如果恶意攻击服务器,每次都在第一次握手中的SYN报文中放入大量数据。而且频繁重复发SYN报文,服务器会花费很多的时间和内存空间去接收这些报文。
第三次握手,此时客户端已经处于ESTABLISHED状态。对于客户端来说,他已经建立起连接了,并且已经知道服务器的接收和发送能力是正常的。所以也就可以携带数据了。
22.为什么String做连接操作很慢?为什么String的每个方法都要重新new String,这样设计为什么?
String做连接操作会产生很多临时String对象。 保证String的不可变性。只要更改String的内容就会让引用指向新的对象。
23.主键和唯键的区别?
-
1、当一个属性声明为主键时,它将不接受NULL值。另一方面,当声明为Unique的属性时,它可以接受一个NULL值。
-
2、表中只能有一个主键,但可以有多个唯一键。
-
3、定义主键时自动创建聚簇索引。相反,Unique键生成非聚簇索引。
24.数据库的悲观锁和乐观锁
-
悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
-
乐观锁 总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。
乐观锁常见的两种实现方式
-
版本号机制 一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。
-
举一个简单的例子: 假设数据库中帐户信息表中有一个 version 字段,当前值为 1 ;而当前帐户余额字段( balance )为 $100 。
-
操作员 A 此时将其读出( version=1 ),并从其帐户余额中扣除 $50( $100-$50 )。
-
在操作员 A 操作的过程中,操作员B 也读入此用户信息( version=1 ),并从其帐户余额中扣除 $20 ( $100-$20 )。
-
操作员 A 完成了修改工作,将数据版本号加一( version=2 ),连同帐户扣除后余额( balance=$50 ),提交至数据库更新,此时由于提交数据版本大于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。
-
操作员 B 完成了操作,也将版本号加一( version=2 )试图向数据库提交数据( balance=$80 ),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 2 ,数据库记录当前版本也为 2 ,不满足 “ 提交版本必须大于记录当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。
这样,就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作员A 的操作结果的可能。
- CAS算法即compare and swap(比较与交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三个操作数
需要读写的内存值 V 进行比较的值 A 拟写入的新值 B 当且仅当 V 的值等于 A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。
乐观锁缺点:
- ABA 问题。如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的 "ABA"问题。
- 循环时间长开销大。自旋CAS(也就是不成功就一直循环执行直到成功)如果长时间不成功,会给CPU带来非常大的执行开销。
- 只能保证一个共享变量的原子操作。CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。
CAS与synchronized的使用情景
- 简单的来说CAS适用于写比较少的情况下(多读场景,冲突一般较少),synchronized适用于写比较多的情况下(多写场景,冲突一般较多)
25.磁盘调页是什么
26.Linux中的fork一般用于什么场景
27.Http是长连接还是短连接
- HTTP1.0时协议默认是短连接,客户端和服务端进行一次http请求就需要建立一次连接,结束后就中断连接。我们再访问网页中含有其他的web资源(图片、js文件),每次去访问这样一个web资源,浏览器就会新建立一个http会话。
- HTTP1.1时协议默认就是长连接了,保持其连接的持续性,在使用http长连接的时候,http相应的响应头会有Connection:keep-alive. 长连接,当浏览器打开网页之后,客户端和服务端之间用于传输HTTP数据的TCP连接通道不会关闭,客户端再次访问服务器时会继续使用已经存在的连接,但是这个Connection:keep-alive也不会永远保持连接,保持的时间是有服务器端设定的,而实现这种长连接是服务端和客户端都支持才行的。 所以HTTP协议的长连接和短连接实质上是TCP协议的长连接和短连接。
28.Http会话Tcp什么时候断开连接,哪一方先关闭。
- 1.对于http1.0协议来说,如果响应头中有content-length头,则以content-length的长度就可以知道body的长度了,客户端在接收body时,就可以依照这个长度来接收数据,接收完后,就表示这个请求完成了。而如果没有content-length头,则客户端会一直接收数据,直到服务端主动断开连接,才表示body接收完了。
- 2.而对于http1.1协议来说,如果响应头中的Transfer-encoding为chunked传输,则表示body是流式输出,body会被分成多个块,每块的开始会标识出当前块的长度,此时,body不需要通过长度来指定。如果是非chunked传输,而且有content-length,则按照content-length来接收数据。否则,如果是非chunked,并且没有content-length,则客户端接收数据,直到服务端主动断开连接。
29.多个生产者和消费者怎么实现生产者生产,消费者消费。
30.JVM加载类的过程。
- 在实际类加载过程中,JVM会将所有的.class字节码文件中的二进制数据读入内存中,导入运行时数据区的方法区中。 当一个类首次被主动加载或被动加载时,类加载器会对此类执行类加载的流程 – 加载、连接(验证、准备、解析)、初始化。 如果类加载成功,堆内存中会产生一个新的Class对象,Class对象封装了类在方法区内的数据结构。
31.数据库连接池的底层实现。
32.可重入锁的原理,AQS.
33.讲一下双亲委派模型
-
核心思想:其一,自底向上检查类是否已加载;其二,自顶向下尝试加载类。 具体加载过程
-
当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
-
当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。
-
如果BootStrapClassLoader加载失败(例如在%JAVA_HOME%/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;
-
如果ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。
34.讲一下Reids持久化机制
- Redis提供了RDB和AOF两种不同的数据持久化方式
35.讲一下AOP做什么用的,怎么实现的。
-
Spring AOP的面向切面编程,是面向对象编程的一种补充,用于处理系统中分布的各个模块的横切关注点,比如说事务管理、日志、缓存等。它是使用动态代理实现的,在内存中临时为方法生成一个AOP对象,这个对象包含目标对象的所有方法,在特定的切点做了增强处理,并回调原来的方法。
-
Spring AOP的动态代理主要有两种方式实现,JDK动态代理和cglib动态代理。JDK动态代理通过反射来接收被代理的类,但是被代理的类必须实现接口,核心是InvocationHandler和Proxy类。cglib动态代理的类一般是没有实现接口的类,cglib是一个代码生成的类库,可以在运行时动态生成某个类的子类,所以,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
36.Spring中配置密码时如何避免密码泄露。
37.Innodb和Myisam的区别。
MyISAM特点
- 不支持行锁(MyISAM只有表锁),读取时对需要读到的所有表加锁,写入时则对表加排他锁;
- 不支持事务
- 不支持外键
- 不支持崩溃后的安全恢复
InnoDB特点
- 支持行锁,采用MVCC来支持高并发,有可能死锁
- 支持事务
- 支持外键
- 支持崩溃后的安全恢复
- 不支持全文索引
38.括号匹配,二叉树的最近公共祖先。
括号匹配
public static boolean isValid(String s) {
if (s.length() == 0) {
return true;
}
HashMap<Character, Character> map = new HashMap<>();
map.put('(', ')');
map.put('[', ']');
map.put('{', '}');
//括号先入后出、遇到左括号压栈,遇到右括号弹出栈顶元素比较
Stack<Character> stack = new Stack<>();
for (int i = 0; i < s.length(); i++) {
if (map.containsKey(s.charAt(i))) {
stack.push(s.charAt(i));
} else {
if (stack.isEmpty() || map.get(stack.pop()) != s.charAt(i)) {
return false;
}
}
}
return stack.isEmpty();
}
二叉树的最近公共祖先
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//递归结束条件,root为null或者root为p,q结点
if (root == null || root == p || root == q) {
return root;
}
//分
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
//如果在root左子树和右子树中都找到了,那么root就是lca
if (left != null && right != null) {
return root;
}
//如果只在root的左子树找到了,向上返回left
else if (left != null) {
return left;
}
//如果指在root的右子树中找到了,向上返回right
else {
return right;
}
}
39.Tcp流量控制解决了什么问题,采用了什么算法
40.Tcp拥塞控制解决了什么问题,采用了什么算法
41.红黑树相比平衡二叉树的优点
42.红黑树相比AVL的优点
43.数据库如何实现回滚到一天前。
44.数据库的备份是如何实现的。
45.数据库的冷备份和热备份
46.B+树非叶子结点存放了什么(索引)
47.B+树一个结点能够存储多少数据。(4KB)
48.B+树如何利用磁盘预读特性。
Mysql设计利用了磁盘预读原理,将一个B+Tree节点大小设为一个页大小(4KB),在新建节点时直接申请一个页的空间,这样就能保证一个节点物理上存储在一个页里,加之计算机存储分配都是按页对齐的,这样就实现了每个Node节点只需要一次I/O操作。
49.数据库加锁加在B+树的哪个地方
50.Mysql查询很慢的原因(查日志explain分析)
-
没有索引或者没有用到索引
-
IO吞吐量小形成了瓶颈。
-
出现死锁
-
内存不足
-
网络速度慢
-
一次查询的数据量过大。进行分页或者多次查询。
51.消息队列的死信队列是什么。
52.spring中哪里用到了工厂模式。
53.进程调度算法有哪些。
- 时间片轮转调度算法(Round Robin):给每个进程固定的执行时间,根据进程到达的先后顺序让进程在单位时间片内执行,执行完成后便调度下一个进程执行,时间片轮转调度不考虑进程等待时间和执行时间,属于抢占式调度。优点是兼顾长短作业;缺点是平均等待时间较长,上下文切换较费时。适用于分时系统。
- 先来先服务调度算法(FCFS):根据进程到达的先后顺序执行进程,不考虑等待时间和执行时间,会产生饥饿现象。属于非抢占式调度,优点是公平,实现简单;缺点是不利于短作业。
- 优先级调度算法:在进程等待队列中选择优先级最高的来执行。
- 多级反馈队列调度算法:将时间片轮转与优先级调度相结合,把进程按优先级分成不同的队列,先按优先级调度,优先级相同的,按时间片轮转。优点是兼顾长短作业,有较好的响应时间,可行性强,适用于各种作业环境。
- 高响应比优先调度算法:根据“响应比=(进程执行时间+进程等待时间)/ 进程执行时间”这个公式得到的响应比来进行调度。高响应比优先算法在等待时间相同的情况下,作业执行的时间越短,响应比越高,满足段任务优先,同时响应比会随着等待时间增加而变大,优先级会提高,能够避免饥饿现象。优点是兼顾长短作业,缺点是计算响应比开销大,适用于批处理系统。
54.XSS攻击是什么,怎么预防。
55.Zookeeper是用来干什么的
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
56.分布式锁的优缺点。
57.Top-K堆排序和快排怎么实现,复杂度是多少。
堆排序,求前K小就构建大顶堆。求前K大就构建小顶堆
时间复杂度:O(nlogk)
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if (k == 0) {
return new int[]{};
}
Queue<Integer> bigHeap = new PriorityQueue<>(k, (i1, i2) -> i2 - i1);
for (int i : arr) {
if (bigHeap.size() < k) {
bigHeap.offer(i);
} else if (i < bigHeap.peek()) {
bigHeap.poll();
bigHeap.offer(i);
}
}
//利用stream特性
return bigHeap.stream().mapToInt(Integer::intValue).toArray();
}
}
快速排序利用partition,时间复杂度是O(N)
N+N/2+N/4+...+N/N=O(2n)=0(n)
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if (arr.length == 0 || k == 0) return new int[]{};
//前K个数的最后一个下标是k-1
return helper(arr, 0, arr.length - 1, k - 1);
}
public int[] helper(int[] arr, int left, int right, int k) {
int index = partition(arr, left, right);
if (index == k) return Arrays.copyOf(arr, index + 1);
return index > k ? helper(arr, left, index - 1, k) : helper(arr, index + 1, right, k);
}
public int partition(int[] arr, int left, int right) {
if (left > right) {
return left;
}
int i = left;
int j = right;
int temp = 0;
int base = arr[left];
while (i < j) {
while (i < j && arr[j] >= base) {
j--;
}
while (i < j && arr[i] <= base) {
i++;
}
if (i < j) {
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
arr[left] = arr[i];
arr[i] = base;
return i;
}
}
58.Shiro相比JWT有什么区别
59.MVCC能解决幻读吗(不能)?
-
MVCC不能解决幻读。MCVV而是解决了读数据情况下的幻读问题。而对于修改的操作依旧存在幻读问题,就是说MVCC对于幻读的解决时不彻底的。
-
通过MVCC机制,虽然让数据变得可重复读,但我们读到的数据可能是历史数据,不是数据库最新的数据。这种读取历史数据的方式,我们叫它快照读 (snapshot read),而读取数据库最新版本数据的方式,叫当前读 (current read)。
-
对于会对数据修改的操作(update、insert、delete)都是采用当前读的模式。在执行这几个操作时会读取最新的记录**,即使是别的事务提交的数据也可以查询到**。假设要update一条记录,但是在另一个事务中已经delete掉这条数据并且commit了,如果update就会产生冲突,所以在update的时候需要知道最新的数据。
如何解决幻读
- 使用串行化读的隔离级别
- MVCC+next-key locks:next-key locks由record locks(索引加锁) 和 gap locks(间隙锁,每次锁住的不光是需要使用的数据,还会锁住这些数据附近的数据)
60.原地删除排序数组中的重复元素
public static int removeDuplicates(int[] nums) {
if (nums.length == 0) {
return 0;
}
int i = 0;
for (int j = 1; j < nums.length; j++) {
if (nums[j] != nums[i]) {
i++;
nums[i] = nums[j];
}
}
return i + 1;
}
61.两个栈实现队列
class CQueue {
private Stack<Integer> stack1;
private Stack<Integer> stack2;
public CQueue() {
stack1 = new Stack<>();
stack2 = new Stack<>();
}
public void appendTail(int value) {
stack1.push(value);
}
public int deleteHead() {
if (!stack2.isEmpty()) {
return stack2.pop();
} else {
if (stack1.isEmpty()) {
return -1;
} else {
while (!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
return stack2.pop();
}
}
}
}
62.RabbitMQ怎么防止数据重复消费
63.反射获取类的方法有哪些(getMethods、getDeclaredMethods)
- getMethods只能获取public方法
- getDeclaredMethods(),该方法是获取本类中的所有方法,包括私有的(private、protected、默认以及public)的方法。
Class<?> string = Class.forName("java.lang.String");
Method[] methods = string.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("===================================");
Method[] declaredMethods = string.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
64.String类设计成不可变的好处(线程安全、常量池提高性能、安全)
1.设计成不可变之后,多线程环境下是线程安全的。 2.比如字符串的常量池节省内存,缓存Hash类以字符串做key数据结构的hashCode,从而提高访问性能等。 3.在安全方面将String设计成不可变的原因就是String被用来进行文件操作,内存管理和网络操作。如果字符串可以被修改,那么很多属性都将可能被恶意修改。
65.Object类有哪些方法
1.clone方法 2.getClass方法 3.toString方法 4.finalize方法 5.equals方法
6.hashCode方法 7.wait方法 8.notify方法 9.notifyAll方法
66.Http怎么设置Header来保持连接。
http 请求header头中通过Connection: 头字段来指定是否需要keep-alive
67.NIO是基于select、poll还是epoll。
NIO是基于epoll的。NIO,同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
68.Http消息解码怎么做的。
69.Mysql主从复制怎么实现
- 主从复制是指将主数据库的DDL和DML操作通过二进制日志传到从数据库上,然后在从数据库上对这些日志进行重新执行,从而使从数据库和主数据库的数据保持一致。
- MySql主库在事务提交时会把数据变更作为事件记录在二进制日志Binlog中;
- 主库推送二进制日志文件Binlog中的事件到从库的中继日志Relay Log中,之后从库根据中继日志重做数据变更操作,通过逻辑复制来达到主库和从库的数据一致性;
- MySql通过三个线程来完成主从库间的数据复制,其中Binlog Dump线程跑在主库上,I/O线程和SQL线程跑着从库上;
- 当在从库上启动复制时,首先创建I/O线程连接主库,主库随后创建Binlog Dump线程读取数据库事件并发送给I/O线程,I/O线程获取到事件数据后更新到从库的中继日志Relay Log中去,之后从库上的SQL线程读取中继日志Relay Log中更新的数据库事件并应用,如下图所示。
70.binlog有哪几种同步方式(三种)
- 基于**SQL语句的复制(**statement-based replication, SBR),
- 基于行的复制(row-based replication, RBR),
- 混合模式复制(mixed-based replication, MBR)。
相应地,binlog的格式也有三种:STATEMENT,ROW,MIXED。 MBR 模式中,SBR 模式是默认的。
71.Linux怎么查看连接状态(ps aus lsof tcpdump)
-
netstat -aulnp| grep tcp
-
lsof -i:端口号
72.Redis缓存过期策略
73.Mysql建立索引之后怎么知道查询是否命中了索引,命中哪个索引。
- 通过explain执行计划来分析查询语句。其中key字段代表实际名中的索引。
explain有以下字段
-
select_type :表示查询中每个select子句的类型(简单 OR复杂)
-
type:all、index、range、ref...null
-
possible_keys:指出MySQL能使用哪个索引在表中找到行,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用
-
key:显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL TIPS:查询中若使用了覆盖索引,则该索引仅出现在key列表中
-
key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度 key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的
-
ref:表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
-
Extra:包含不适合在其他列中显示但十分重要的额外信息