1、SpingBoot 也有定时任务?是什么注解?
在 SpringBoot 中使用定时任务主要有两种不同的方式,一个就是使用 Spring 中的
@Scheduled
注解,另一个则是使用第三方框架 Quartz。
使用 Spring 中的
@Scheduled
的方式主要通过
@Scheduled
注解来实现。
使用 Quartz ,则按照 Quartz 的方式,定义 Job 和 Trigger 即可。
2、请描述线程的生命周期,它们之间如何切换?
线程的生命周期包含 5 个阶段,包括:新建、就绪、运行、阻塞、销毁。
⚫
新建(NEW):就是刚使用 new 方法,new 出来的线程;
⚫
就绪(RUNNABLE):就是调用的线程的 start()方法后,这时候线程处于等待 CPU 分配
资源阶段,谁先抢的 CPU 资源,谁开始执行;
⚫
运行(RUNNING):当就绪的线程被调度并获得 CPU 资源时,便进入运行状态,run 方
法定义了线程的操作和功能;
⚫
阻塞(BLOCKED):在运行状态的时候,可能因为某些原因导致运行状态的线程变成了
阻塞状态,比如 sleep()、wait()之后线程就处于了阻塞状态,这个时候需要其他机制将处
于阻塞状态的线程唤醒,比如调用 notify 或者 notifyAll()方法。唤醒的线程不会立刻执行
run 方法,它们要再次等待 CPU 分配资源进入运行状态;
⚫
Waiting(无限等待):一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进
入 Waiting 状态。进入这个状态后不能自动唤醒,必须等待另一个线程调用 notify 方法
或者 notifyAll 方法时才能够被唤醒。
⚫
销毁(TERMINATED):如果线程正常执行完毕后或线程被提前强制性的终止或出现异
常导致结束,那么线程就要被销毁,释放资源;
3、什么情况线程会进入 WAITING 状态?
一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入 Waiting 状态。进入这个
状态后不能自动唤醒,必须等待另一个线程调用 notify 方法或者 notifyAll 方法时才能够被唤
醒。
⚫
调用 Object 对象的 wait 方法,但没有指定超时值。
⚫
调用 Thread 对象的 join 方法,但没有指定超时值。
⚫
调用 LockSupport 对象的 park 方法。
4、简述多进程开发中 join 和 deamon 的区别?
join:当子线程调用 join 时,主线程会被阻塞,当子线程结束后,主线程才能继续执行。
deamon:当子进程被设置为守护进程时,主进程结束,不管子进程是否执行完毕,都会随着
主进程的结束而结束。
5、异步和同步、阻塞和非阻塞之间的区别?
同步
当一个 request 发送出去以后,会得到一个 response,这整个过程就是一个同步调用的过
程。哪怕 response 为空,或者 response 的返回特别快,但是针对这一次请求而言就是一个
同步的调用。
异步
当一个 request 发送出去以后,没有得到想要的 response,而是通过后面的 callback、状态
或者通知的方式获得结果。可以这么理解,对于异步请求分两步:
⚫
调用方发送 request 没有返回对应的 response(可能是一个空的 response);
⚫
服务提供方将 response 处理完成以后通过 callback 的方式通知调用方。
对于 1)而言是同步操作(调用方请求服务方),对于 2)而言也是同步操作(服务方回掉调
用方)。从请求的目的(调用方发送一个 request,希望获得对应的 response)来看,这两
个步骤拆分开来没有任何意义,需要结合起来看,而这整个过程就是一次异步请求。异步请求
有一个最典型的特点:需要 callback、状态或者通知的方式来告知调用方结果。
阻塞
阻塞调用是指调用方发出 request 的线程因为某种原因(如:等待系统资源)被服务方挂
起,当服务方得到 response 后就唤醒挂起线程,并将 response 返回给调用方。
非阻塞
非阻塞调用是指调用方发出 request 的线程在没有等到结果时不会被挂起,并且直到得到
response 后才返回。
阻塞和非阻塞最大的区别就是看调用方线程是否会被挂起。
6、为什么要分内核态和用户态?
假设没有这种内核态和用户态之分,程序随随便便就能访问硬件资源,比如说分配内存,程序
能随意的读写所有的内存空间,如果程序员一不小心将不适当的内容写到了不该写的地方,就
很可能导致系统崩溃。用户程序是不可信的,不管程序员是有意的还是无意的,都很容易将系
统干到崩溃。
正因为如此,Intel 就发明了 ring0-ring3 这些访问控制级别来保护硬件资源,ring0 的就是我
们所说的内核级别,要想使用硬件资源就必须获取相应的权限(设置 PSW 寄存器,这个操作只
能由操作系统设置)。操作系统对内核级别的指令进行封装,统一管理硬件资源,然后向用户
程序提供系统服务,用户程序进行系统调用后,操作系统执行一系列的检查验证,确保这次调
用是安全的,再进行相应的资源访问操作。**内核态能有效保护硬件资源的安全。
7、说下类加载器与类加载?加载的类信息放在哪个区域?
一个类型从被加载到虚拟机内存开始,到卸载出内存为止,它的整个生命周期将会经历加载
(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化
(Initialization)、使用(Using)和卸载(Unloading)七个阶段。其中验证、准备、解析
三个部分统称为连接(Linking)。
Java 虚拟机设计团队把类加载阶段中“
通过一个类的全限定名来获取描述该类的二进制流
”
这个动作放到 Java 虚拟机外部去实现。比便让程序应用自己决定如何取获取所需的类。实现
这个动作的代码被称为“
类加载器
”(Class Loader)。
对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在 Java 虚拟机中
的唯一性,每一个类加载器,都拥有一个独立的类名称空间。
8、UDP 协议和 TCP 协议的区别?
⚫
TCP 基于连接,UDP 基于无连接
⚫
TCP 要求系统资源较多,UDP 较少
⚫
UDP 程序结构较简单
⚫
TCP 保证数据正确性,UDP 可能丢包
⚫
TCP 保证数据顺序,UDP 不保证
9、limit 1000000 加载很慢的话,你是怎么解决的呢?
方案一:如果 id 是连续的,可以这样,返回上次查询的最大记录(偏移量),再往下 limit
select id,name from employee where id>1000000 limit 10.
方案二:在业务允许的情况下限制页数:
建议跟业务讨论,有没有必要查这么后的分页啦。因为绝大多数用户都不会往后翻太多页。
方案三:order by + 索引(id 为索引)
select id,name from employee order by id limit 1000000,10
方案四:利用延迟关联或者子查询优化超多分页场景。(先快速定位需要获取的 id 段,然后
再关联)
SELECT a.* FROM employee a, (select id from employee where 条件 LIMIT 1000000,10 ) b
where a.id=b.id
10、MySQL 的索引分类是什么?
单列索引
⚫
普通索引:MySQL 中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值
和空值,纯粹为了查询数据更快一点。
⚫
唯一索引:索引列中的值必须是唯一的,但是允许为空值,
⚫
主键索引:是一种特殊的唯一索引,不允许有空值。
组合索引:
多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使
用,使用组合索引时遵循最左前缀集合。
全文索引:
只有在 MyISAM 引擎上才能使用,只能在 CHAR,VARCHAR,TEXT 类型字段上使用全文索
引,介绍了要求,说说什么是全文索引,就是在一堆文字中,通过其中的某个关键字等,就能
找到该字段所属的记录行,比如有"你是个靓仔,靓女 ..." 通过靓仔,可能就可以找到该条记
录
空间索引:
空间索引是对空间数据类型的字段建立的索引,MySQL 中的空间数据类型有四种,
GEOMETRY、POINT、LINESTRING、POLYGON。在创建空间索引时,使用 SPATIAL 关键
字。要求,引擎为 MyISAM,创建空间索引的列,必须将其声明为 NOT NULL。
11、什么是散列表? select * 和 select 1?
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结
构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个
映射函数叫做散列函数,存放记录的数组叫做散列表。
有时候为了提高效率,只是为了测试下某个表中是否存在记录,就用 1 来代替。
12、MySQL 的主从复制了解吗?
主库将变更写入 binlog 日志,然后从库连接到主库之后,从库有一个 IO 线程,将主库的
binlog 日志拷贝到自己本地,写入一个 relay 中继日志中接着从库中有一个 SQL 线程会从中
继日志读取 binlog,然后执行 binlog 日志中的内容,也就是在自己本地再次执行一遍 SQL。
13、Spring 框架事务注解用什么注解?使用该注解的失效场景?
@Transactional
⚫
Transactional 注解应用在非 public 修饰的方法上@Transactional 注解属性
propagation 设置错误
⚫
@Transactional 注解属性 rollbackFor 设置错误
⚫
同一个类中方法调用,导致@Transactional 失效
⚫
异常被 catch“吃了”导致@Transactional 失效
14、final、finally、finallize?finally 是在 return 之前执行还是之后?
finally 块里的代码一定会执行吗?
⚫
final 可以用来修饰类、方法、变量,分别有不同的意义,final 修饰的 class 代表不可以
继承扩展,final 的变量是不可以修改的,而 final 的方法也是不可以重写的
(override)。
⚫
finally 是 Java 保证重点代码一定要被执行的一种机制。可以使用 try-finally 或者 try
catch-finally 来进行类似关闭 JDBC 连接、保证 unlock 锁等动作。
⚫
finalize 是基础类 java.lang.Object 的一个方法,设计目的是保证对象在被垃圾收集前完
成特定资源的回收。finalize 机制现在已经不推荐使用,并且在 JDK 9 开始被标记为
deprecated。
finally 块的语句在 try 或 catch 中的 return 语句执行之后返回之前执行且 finally 里的修改语
句可能影响也可能不影响 try 或 catch 中 return 已经确定的返回值,若 finally 里也有 return
语句则覆盖 try 或 catch 中的 return 语句直接返回。
finally 块里的代码不一定会执行。比如:
⚫
try 语句没有被执行到,如在 try 语句之前就返回了,这样 finally 语句就不会执行,这也
说明了 finally 语句被执行的必要而非充分条件是:相应的 try 语句一定被执行到。
⚫
在 try 块中有 System.exit(0**
15、I/O 多路复用实现方式有哪些?
⚫
select
⚫
poll
⚫
epoll
16、select、poll、epoll 区别有哪些?
select:它仅仅知道了,有 I/O 事件发生了,却并不知道是哪那几个流(可能有一个,多个,
甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行
操作。所以
select 具有 O(n)的无差别轮询复杂度
,同时处理的流越多,无差别轮询时间就越
长。
poll:poll 本质上和 select 没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个
fd 对应的设备状态,
但是它没有最大连接数的限制
,原因是它是基于链表来存储的.
epoll:
epoll 可以理解为 event poll
,不同于忙轮询和无差别轮询,epoll 会把哪个流发生了
怎样的 I/O 事件通知我们。所以我们说 epoll 实际上是
事件驱动(每个事件关联上 fd)
的,
此时我们对这些流的操作都是有意义的。(复杂度降低到了 O(1)),
通过红黑树和双链表数
据结构,并结合回调机制,造就了 epoll 的高效
,epoll_create(),epoll_ctl()和 epoll_wait()
系统调用。
17、哈希算法解决哈希冲突方式有哪些?
解决哈希冲突的方法一般有:开放寻址法、链地址法(拉链法)、再哈希法、建立公共溢出区
等方法。
18、如何保证 Redis 中的数据不丢失?
单机单节点模式
使用 AOF 和 RDB 结合的方式
RDB 做镜像
全量持久化
,AOF 做
增量持久化
。因为 RDB 会耗费较长时间,不够实时,在停
机的时候会导致大量丢失数据,所以需要 AOF 来配合使用。
Redis 集群模式
master 节点持久化
如果采用了主从架构,那么建议必须开启 master node 的持久化!不建议用 slave node 作
为 master node 的数据热备,因为那样的话,如果你关掉 master 的持久化,可能在 master
宕机重启的时候数据是空的,然后可能一经过复制,salve node 数据也丢了,master 就会将
空的数据集同步到 slave 上去,所有 slave 的数据全部清空。
Redis 断点续传
从 redis 2.8 开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那
么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份。
主备切换的过程,可能会导致数据丢失
解决异步复制和脑裂导致的数据丢失
redis.conf 中
min-slaves-to-write 1
min-slaves-max-lag 10
要求至少有 1 个 slave,数据复制和同步的延迟不能超过 10 秒
如果说一旦所有的 slave,数据复制和同步的延迟都超过了 10 秒钟,那么这个时候,master
就不会再接收任何请求了
上面两个配置可以减少异步复制和脑裂导致的数据丢失。
19、如何保证 Redis 中的数据都是热点数据?
⚫
Redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。Redis 提供 6 种
数据淘汰策略:
⚫
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的
数据淘汰
⚫
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据
淘汰
⚫
volatile-random:从已设置过期时间的数据集(
server.db[i].expires)中任意选择数据
淘汰
⚫
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
⚫
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
⚫
no-enviction(驱逐):禁止驱逐数据
20、Redis 持久化机制是如何做的?
RDB
RDB 持久化方式,是将 Redis 某一时刻的数据持久化到磁盘中,是一种快照式的持久化方
法。
RDB 优点:
⚫
RDB 是一个非常紧凑(有压缩)的文件,它保存了某个时间点的数据,非常适用于数据的备
份。
⚫
RDB 作为一个非常紧凑(有压缩)的文件,可以很方便传送到另一个远端数据中心 ,非
常适用于灾难恢复.
⚫
RDB 在保存 RDB 文件时父进程唯一需要做的就是 fork 出一个子进程,接下来的工作全部
由子进程来做,父进程不需要再做其他 IO 操作,所以 RDB 持久化方式可以最大化 redis
的性能.
⚫
与 AOF 相比,在恢复大的数据集的时候,RDB 方式会更快一些.
RDB 缺点:
⚫
Redis 意外宕机 时,会丢失部分数据
⚫
当 Redis 数据量比较大时,fork 的过程是非常耗时的,fork 子进程时是会阻塞的,在这
期间 Redis 是不能响应客户端的请求的。
AOF
AOF 方式是将执行过的写指令记录下来,在数据恢复时按照从前到后的顺序再将指令都执行
一遍。
AOF 优点:
⚫
使用 AOF 会让你的 Redis 更加持久化。
⚫
AOF 文件是一个只进行追加的日志文件,不需要在写入时读取文件。
⚫
Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写 。
⚫
AOF 文件可读性高,分析容易。
AOF 缺点:
⚫
对于相同的数据来说,AOF 文件大小通常要大于 RDB 文件
⚫
根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB
混合持久化方式
Redis 4.0 之后新增的方式,混合持久化是结合了 RDB 和 AOF 的优点,在写入的时候,先把
当前的数据以 RDB 的形式写入文件的开头,再将后续的操作命令以 AOF 的格式存入文件,
这
样既能保证 Redis 重启时的速度,又能减低数据丢失的风险。
21、Redis 为什么在使用 RDB 进行快照时会通过子进程的方式进行实现?
⚫
通过 fork 创建的子进程能够获得和父进程完全相同的内存空间,父进程对内存的修改对
于子进程是不可见的,两者不会相互影响;
⚫
通过 fork 创建子进程时不会立刻触发大量内存的拷贝,内存在被修改时会以页为单位进
行拷贝,这也就避免了大量拷贝内存而带来的性能问题;
22、介绍下 MySQL 的主从复制原理?产生主从延迟的原因?
⚫
主从复制原理: 主库将变更写入 binlog 日志,然后从库连接到主库之后,从库有一个 IO
线程,将主库的 binlog 日志拷贝到自己本地,写入一个 relay 中继日志中。 接着从库中
有一个 SQL 线程会从中继日志读取 binlog,然后执行 binlog 日志中的内容,也就是在
自己本地再次执行一遍 SQL。
⚫
主从延迟:
a. 主库的从库太多
b. 从库硬件配置比主库差
c. 慢 SQL 语句过多
d. 主从库之间的网络延迟
e. 主库读写压力大
23、父进程如果宕掉,子进程会怎样?
如果父进程是会话首进程,那么父进程退出后,子进程也会退出;反之如果父进程不是会话首
进程,那么父进程退出后,子进程不会退出,而它的一个或多个子进程还在运行,那么这些子
进程就成为孤儿进程。
24、孤儿进程和僵尸进程有什么区别?
孤儿进程:父进程结束了,而它的一个或多个子进程还在运行,那么这些子进程就成为孤儿进
程(father died)。子进程的资源由 init 进程(进程号 PID = 1)回收。
僵尸进程:子进程退出了,但是父进程没有用 wait 或 waitpid 去获取子进程的状态信息,那
么子进程的进程描述符仍然保存在系统中,这种进程称为僵死进程。
25、MySQL 中有哪几种锁?
⚫
表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度
最低。
⚫
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度
也最高。
⚫
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之
间,并发度一般。
26、互斥锁(mutex)和自旋锁(spinlock)分别在什么场景使用?
在多核机器中,如果锁住的“事务”很简单,占用很少的时间,就应该使用 spinlock,这个
时候 spinlock 的代价比 mutex 会小很多。”事务”很快执行完毕,自旋的消耗远远小于陷入
sleep 和 wake 的消耗。如果锁住“事务”粒度较大,就应该使用 mutex,因为如果用
spinlock,那么在“事务”执行过程中自旋很长时间还不如使得线程 sleep。
在单核机器中。spinlock 没有任何意义的,spinlock 只会浪费唯一核心的 cpu 时间片,这个
时刻没有任何线程会运行的。所以单核机器中,不论锁住的”事务”的粒度大小都要使用。
27、描述 Synchronized、ReentrantLock 的区别 ?
⚫
synchronized 是关键字,ReentrantLock 是 API 接口
⚫
Lock 需要手动加锁,手动释放锁
⚫
synchronized 不可中断,ReentrantLock 可中断、可超时
⚫
synchronized 是非公平锁,ReentrantLock 公平、非公平皆可
⚫
ReentrantLock 支持 Condition,多条件
28、HashMap 扩容操作是怎么实现的?
⚫
在 jdk1.8 中,resize 方法是在 hashmap 中的键值对大于阀值时或者初始化时,就调用
resize 方法进行扩容;
⚫
每次扩展的时候,都是扩展 2 倍;
⚫
扩展后 Node 对象的位置要么在原位置,要么移动到原偏移量两倍的位置。
29、ConcurrentHashMap 1.7 与 1.8 区别?
⚫
1.8 采用 synchronized 代替可重入锁 ReentrantLock (现代 JDK 中,synchronized 已
经被不断优化,可以不再过分担心性能差异)
⚫
1.8 取消了 Segment 分段锁的数据结构,使用数组+链表+红黑树的结构代替
⚫
1.8 对每个数组元素加锁,1.7 对要操作的 Segment 数据段加锁
30、如何使用 Java 的反射?
⚫
通过一个全限类名创建一个对象
Class.forName(“全限类名”); 例如:com.mysql.jdbc.Driver Driver 类已经被加载到 jvm
中,并且完成了类的初始化工作就行了
⚫
类名.class; 获取 Class<?> clz 对象
对象.getClass();
⚫
获取构造器对象,通过构造器 new 出一个对象
Clazz.getConstructor([String.class]);
Con.newInstance([参数]);
⚫
通过 class 对象创建一个实例对象(就相当与 new 类名()无参构造器)
Cls.newInstance();
⚫
通过 class 对象获得一个属性对象
Field c=cls.getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。
Field c=cls.getDeclaredFields():获得某个类的所有声明的字段,即包括 public、private
和 proteced,但是不包括父类的声明字段
⚫
通过 class 对象获得一个方法对象
Cls.getMethod(“方法名”,class……parameaType);(只能获取公共的)
Cls.getDeclareMethod(“方法名”);(获取任意修饰的方法,不能执行私有)
M.setAccessible(true);(让私有的方法可以执行)
⚫
让方法执行
Method.invoke(obj 实例对象,obj 可变参数);-----(是有返回值的)