java面试题

勤奋不是嘴上说说而已,而是实际的行动,在勤奋的苦度中持之以恒,永不退却。业精于勤,荒于嬉;行成于思,毁于随。在人生的仕途上,我们毫不迟疑地选择勤奋,她是几乎于世界上一切成就的催产婆。只要我们拥着勤奋去思考,拥着勤奋的手去耕耘,用抱勤奋的心去对待工作,浪迹红尘而坚韧不拔,那么,我们的生命就会绽放火花,让人生的时光更加的闪亮而精彩。

导读:本篇文章讲解 java面试题,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

Bean的生命周期:

  1. 创建
  2. 对bean进行配置
  3. 如果实现了BeanNameAware接口,会调用setBeanName方法,参数是spring配置文件中bean的id属性值
  4. 如果实现了BanFactoryAware接口,会调用setBeanFactory方法,传入工厂自身
  5. 如果实现了BeanApplicationContext接口,会调用setApplicationContext方法,传入spring上下文
  6. 如果实现了BeanPostProcessor接口,会调用器postProcessBeforeInitialization方法
  7. 如果在spring配置bean的时候,指定了init-method属性,就会调用其配置的初始化方法
  8. 如果实现了BeanPostProcessor接口,会调用其postProcessAfterInitialization方法
  9. bean不在需要时,会经过清理阶段,如果实现了DisponsableBean接口,会调用其实现的destory方法
  10. 最后,如果在spring配置Bean的时候,指定了destory-method属性,就会调用其配置的销毁方法

在什么情况下选择用ArrayList or LinkedList

要在List后面添加删除元素和随机访问元素,则选择ArrayList更好,如果要在List前面或中间添加删除元素或者按顺序访问元素,则选择LinkedList更好

如何判断一个对象是否存活(如何对GC对象存活的判断方式)

引用计数算法:为每一个对象创建一个引用计数器,每当一个地方引用改对象,计数器加一,当对象引用失效时,计数器减一,当计数器为0的对象,那么该对象是不能引用的,这个算法的缺点是对象相互循环引用,计数器永远不为0
可达性分析算法:一个GC Roots对象为起点,从这些节点向下搜索,这个路径被称为引用链,当对象没有被引用链相连时,该对象是不可取的
java对象通过可达性分析算法并不是马上死亡,还有一个过程看有没有必要调用finalize()方法,当这个方法已经被虚拟机调用过,或者这个对象没有重写finalize()方法,则证明这个对象已经死亡了
如果这个对象有必要执行finalize()方法,那么这个对象就会放在一个F Queue队列中,会去执行它,这里的执行不是等它调用完毕,而是触碰它,原因是万一在finalize()方法中出现死循环,或者执行缓慢,有可能导致这个F Queue队列永远被等待,甚至内存直接崩溃
回收对象有无用的类(①.这个类的实现对象已被回收,②.这个类的Class对象不能反射调用获取其方法属性,③.这个类ClassLoader对象已被回收)、废弃的字符串

HashMap的原理

HashMap的底层是数组+链表/红黑树,默认容量为16,加载因子为0.75,键值对完全可以把value看做key的附属,进行push时,会对这个key进行Hashcode()决定存储位置,如果另一个键值对的key发生hash碰撞时,这时在存储位置构建一个链表,jdk7采用头插法,jdk8采用尾插法,在jdk8中,当链表的节点个数达到8时,会转成红黑树结构,当红黑树节点降为6时,会重新变成链表,当数组的元素达到16的0.75倍时,扩大到原来的2倍,会对key进行reHash()存储。

TCP,HTTP

三次握手,四次挥手

三次握手

客户端发送带有SYN标志的数据包到服务端

服务端发送带有SYN/ACK标志的数据包到客户端

客户端发送带有ACK标志的数据包到服务端

四次挥手

客户端发送一个FIN,用来关闭客户端到服务端的数据传输

服务端收到这个FIN,发回一个ACK,确认序号为收到的序号加一,和SYN一样,一个FIN占用一个序号

服务端关闭与客户端的连接,发送一个FIN到客户端

客户端收到ACK报文确认,并把确认序号设置为收到序号加一

TCP UDP

TCP

面向连接的,能正确处理丢包,保证数据有序,有一个致命缺点连接和关闭时,要发送7个请求造成资源浪费

UDP

面向非连接的,只管发送,不管收没收到

状态码

2XX 成功

  • 200 OK,表示从客户端发来的请求在服务器端被正确处理
  • 204 No content,表示请求成功,但响应报文不含实体的主体部分
  • 206 Partial Content,进行范围请求

3XX 重定向

  • 301 moved permanently,永久性重定向,表示资源已被分配了新的 URL
  • 302 found,临时性重定向,表示资源临时被分配了新的 URL
  • 303 see other,表示资源存在着另一个 URL,应使用 GET 方法定向获取资源
  • 304 not modified,表示服务器允许访问资源,但因发生请求未满足条件的情况
  • 307 temporary redirect,临时重定向,和302含义相同

4XX 客户端错误

  • 400 bad request,请求报文存在语法错误
  • 401 unauthorized,表示发送的请求需要有通过 HTTP 认证的认证信息
  • 403 forbidden,表示对请求资源的访问被服务器拒绝
  • 404 not found,表示在服务器上没有找到请求的资源

5XX 服务器错误

  • 500 internal sever error,表示服务器端在执行请求时发生了错误
  • 503 service unavailable,表明服务器暂时处于超负载或正在停机维护,无法处理请求

输入url到网页到底发生了什么

  1. 输入url

  2. 获取域名对应的IP地址

  3. 浏览器向web服务器发送一个HTTP请求

  4. 服务器永久重定向响应

  5. 浏览器跟踪重定向地址

  6. 服务器处理请求

  7. 服务器返回HTTP响应

  8. 浏览器显示HTML

  9. 浏览器发送请求获取嵌入在网页中的资源

  10. 浏览器发送异步请求

HTTP请求格式

  1. 起始行

  2. 消息头

  3. 消息体

线程池内部工作原理

线程创建

public ThreadPoolExecutor(
    int corePoolSize,
    int maximumPoolSize,
    long keepAliveTime,
    TimeUnit unit,
    BlockingQueue<Runnable> workQueue,
    ThreadFactory threadFactory,
    RejectedExecutionHandler handler
);
  • corePoolSize:线程池的核心线程数,说白了就是,即便是线程池里没有任何任务,也会有corePoolSize个线程在候着等任务。
  • maximumPoolSize:最大线程数,不管你提交多少任务,线程池里最多工作线程数就是maximumPoolSize。
  • keepAliveTime:线程的存活时间。当线程池里的线程数大于corePoolSize时,如果等了keepAliveTime时长还没有任务可执行,则线程退出。
  • unit:这个用来指定keepAliveTime的单位,比如秒:TimeUnit.SECONDS。
  • workQueue:一个阻塞队列,提交的任务将会被放到这个队列里。
  • threadFactory:线程工厂,用来创建线程,主要是为了给线程起名字,默认工厂的线程名字:pool-1-thread-3。
  • handler:拒绝策略,当线程池里线程被耗尽,且队列也满了的时候会调用。

线程池执行流程

任务被提交到线程池,会先判断当前线程数量是否小于corePoolSize,如果小于则创建线程来执行提交的任务,否则将任务放入workQueue队列,如果workQueue满了,
则判断当前线程数量是否小于maximumPoolSize,如果小于则创建线程执行任务,否则就调用handler,以表示线程池拒绝接收任务
在这里插入图片描述

Java锁

公平锁和非公平锁

  • 公平锁是指多个线程按照申请锁的顺序来获取锁
  • 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象

可重入锁(Reentrant Lock )

又称递归锁,是指在同一个线程在完成方法获取锁的时候,在进入内层方法会自动获取锁

synchronized就是一种可重入锁

共享锁和独享锁

  • 独享锁是指该锁一次只能被一个线程锁持有,可重入锁和synchronized是独享锁
  • 共享锁是指该锁可被多个线程所持有,ReadWriteLock其读是共享锁,其写是独享锁

互斥锁和读写锁

独享锁和共享锁就是一种广义的说法,互斥锁和读写锁是具体的实现

  • ReentrantLock就是互斥锁的实现
  • ReadWriteLock就是读写锁的实现

乐观锁和悲观锁

看到并发同步的角度

  • 悲观锁任务同一个数据的并发操作,一定是会发生修改的,哪怕没有相关,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题,在java中使用就是利用各种锁
  • 乐观锁认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有问题的,在Java中使用就是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的关系

自旋锁

在java中,自旋锁是指测试获取锁的线程不会立即阻塞,而是采用循环的方式 去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU

偏向锁、轻量级锁和重量级锁

指锁的状态

  • 偏向锁是指一段代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价
  • 轻量级锁是指当锁是偏向锁的时候,被另一个线程锁访问,偏向锁升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能
  • 重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低

MySQL建索引有哪些原则

  1. 选择唯一性索引
  2. 为经常需要排序、分组和联合操作的字段建立索引
  3. 为常作为查询条件的字段建立索引
  4. 限制索引的数目
  5. 尽量使用数据量少的索引
  6. 尽量使用前缀来索引
  7. 删除不再使用或者很少使用的索引
  8. 最左前缀匹配原则:MySQL会一直向右匹配直到遇到范围查询(> < between like)就停止匹配
  9. =和in可以乱序
  10. 尽量选择区分度高的列作为索引:区分度公式是

    c

    o

    u

    n

    t

    (

    d

    i

    s

    t

    i

    n

    c

    t

    c

    o

    l

    )

    /

    c

    o

    u

    n

    t

    (

    )

    count(distinct \quad col) / count(*)

    count(distinctcol)/count(),表示字段不重复的比例

  11. 索引项不能参与计算,保持列“干净”
  12. 尽量的扩展索引,不要新建索引

选择索引的目的是为了使查询的速度变快

MySQL面试题

MySQL中myisam与innoDB的区别

myisam

  • 不支持事务,但是每次查询都是原子的
  • 支持表级锁,即每次操作对整个表加锁
  • 存储表的总行数
  • 一个myisam表有三个文件:索引文件,表结构文件,数据文件
  • 采用非聚焦索引,索引文件的数据域存储指向数据文件的指针。辅索引与主索引基本一致,但是辅索引不用保证唯一性

InnoDB

  • 支持ACID的事务,支持事务的四种隔离级别
  • 支持行级锁及外键约束:因此可以支持写并发
  • 不存储总行数
  • 一个InnoDB引擎存储在一个文件空间(共享表空间,表大小不受操作系统控制,一个表可能分布在多个文件里),也有可能为多个(设置为独立表空,表大小受操作系统限制,一般为2G),受操作系统文件大小的限制
  • 主键索引采用聚焦索引(索引数据域存储数据文件本身),辅索引的数据域存储主键的值;因此从辅索引查找数据,需要先通过辅索引找到主键值,再访问辅索引;最好使用自增主键,赋值插入数据时,为维持B+树结构,文件大调整

MySQL int和char隐式类型转换需要注意什么

  1. 当查询字段为int类型,如果查询条件为char,将查询条件转换为int,如果是字符串前导都是数字,将截取前导数字用来比较,如果没有前导数字,则转换为0
  2. 当查询字段为char/varchar类型,如果查询条件为int,将查询字段转换为int在进行比较,可能会造成全表扫描
id name
1 apple
2 banana
3 99cat

case1:

mysql> select * from product where id = '1abc23';
id name
1 apple

case2

mysql> select * from product where name = 0;
id name
1 apple
2 banana

MySQL如何高效率随机获取N条数据

假设表叫做mm_account。

ID连续的情况下(注意不能带where,否则结果不好):

SELECT *
FROM `mm_account` AS t1 JOIN (SELECT ROUND(RAND() * (SELECT MAX(id) 
        FROM `mm_account`)) AS id) AS t2
WHERE t1.id >= t2.id
ORDER BY t1.id ASC LIMIT 4;

ID不连续的情况下:

SELECT * FROM `mm_account` 
WHERE id >= (SELECT floor(RAND() * (SELECT MAX(id)
        FROM `mm_account`)))  and city="city_91" and showSex=1
ORDER BY id LIMIT 4;

如果有一个字段叫id,最快的方法如下(随机获取5条):

SELECT * FROM mm_account 
WHERE id >= ((SELECT MAX(id) FROM mm_account)
        - (SELECT MIN(id) FROM mm_account)) * RAND()
		+ (SELECT MIN(id) FROM mm_account)
limit 5;

如果带where语句,上面就不适合了,带where语句请看下面:

SELECT *
FROM `mm_account` AS t1 JOIN (SELECT ROUND(RAND() * (
(SELECT MAX(id) FROM `mm_account` where id<1000 )-(SELECT MIN(id)
        FROM `mm_account` where id<1000 ))
        +(SELECT MIN(id) FROM `mm_account` where id<1000 )) AS id) AS t2
WHERE t1.id >= t2.id
ORDER BY t1.id LIMIT 5;

MySQL索引类型及应用

普通索引:没有任何限制条件的索引,该索引可以在任何数据类型中创建

唯一索引:使用unique参数可以设置唯一索引。创建该索引时,索引项的值必须唯一,但允许有空值。通过唯一索引,用户可以快速地定位某条记录,主键索引是一种特殊的唯一索引

全文索引:仅可用于MyISAM表,针对较大的数据,生成全文索引,耗时耗空间

空间索引:只能建立在空间数据类型上。这样可以提高系统获取空间数据类型的效率。仅可用于MyISAM表,索引的字段不能为空值。使用SPATIAL参数可以设置索引为空间索引

单列索引:只对应一个字段的索引

多列索引:在表的多个字段上创建一个使用。该索引指向创建时对应的多个字段,用户可以通过这几个字段进行查询,想使用该索引,用户必须使用这些字段中的一个字段

MySQL优点

  1. 运行速度快
  2. 易使用
  3. SQL语言支持
  4. 移植性好
  5. 功能丰富
  6. 成本低廉

索引

数据库是收集表的集合,数据表是数据行和数据列的集合

提高查询速度的技术有很多,其中最重要的就是索引

索引的使用是影响查询速度的重要因素

操作索引

创建索引
-- Case 1
create index indexName on tableName(columnName(length));
-- Case 2
alter table tableName add index indexName(columnName, ...);
-- Case 3
create table tableName(
	...,
    index [indexName] (columnName(length))
);
查看索引
show index from tableName;
删除索引
alter table tableName drop index indexName(columnName);

索引的使用原则

  1. 写操作比较频繁的列慎重加索引

  2. 索引越多占用磁盘空间越大

  3. 不要为输出的列加索引

  4. 考虑维度优势:维度越高(维度的最大值就是数据行的总数),数据列包含的独一无二的值就越多,索引的使用效果就越好

  5. 对短小的值加索引

  6. 为字符串前缀加索引

  7. 复合索引的左侧索引

  8. 索引加锁

  9. 覆盖索引

    原因如下:

    • 索引通常比记录要小,覆盖索引查询只需要读索引,而不需要记录索引
    • 索引都按照值的大小进行顺序存储,相比与随机访问记录,需要更少的I/O
    • 大多数数据引擎能更好的缓存索引,例如MyISAM只缓存索引
  10. 聚簇索引

  11. 选择合适的索引类型

查询优化建议

使用explain分析查询语句
select_type
  • SIMPLE
    简单SELECT,不使用UNION或子查询等。
  • PRIMARY
    查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY。
  • UNION
    UNION中的第二个或后面的SELECT语句。
  • DEPENDENT UNION
    UNION中的第二个或后面的SELECT语句,取决于外面的查询。
  • UNION RESULT
    UNION的结果。
  • SUBQUERY
    子查询中的第一个SELECT。
  • DEPENDENT SUBQUERY
    子查询中的第一个SELECT,取决于外面的查询。
  • DERIVED
    派生表的SELECT, FROM子句的子查询。
  • UNCACHEABLE SUBQUERY
    一个子查询的结果不能被缓存,必须重新评估外链接的第一行。
type

type表示MySQL在表中找到所需行的方式,又称“访问类型”,常用的类型有:

ALL, index, range, ref, eq_ref, const, system, NULL。

从左到右,性能从差到好。

  • ALL:
    Full Table Scan,MySQL将遍历全表以找到匹配的行。
  • index:
    Full Index Scan,index与ALL区别为index类型只遍历索引树。
  • range:
    只检索给定范围的行,使用一个索引来选择行。
  • ref:
    表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值。
  • eq_ref:
    类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件。
  • const:
    当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。
    如将主键置于where列表中,MySQL就能将该查询转换为一个常量。
  • NULL:
    MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。
Key

key列显示MySQL实际决定使用的键(索引),如果没有选择索引,键是NULL。

possible_keys

possible_keys指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上如果存在索引则该索引将被列出,但不一定被查询使用。

ref

ref表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值。

rows

rows表示MySQL根据表统计信息,以及索引选用的情况,找到所需记录需要读取的行数。这个行数是估算的值,实际行数可能不同。

声明not null
考虑使用数值类型代替字符串
考虑使用enum类型

索引是一个单独的,存储在磁盘上的数据结构,索引对数据表中的一列或者多列值进行排序,索引包含着对数据表中所有数据的引用指针

MySQL索引

存储引擎的比较

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SQLam1AC-1621440867195)(e:/typora/index.png)]

在MySQL中,主要有四种类型的索引

  • B-Tree索引

    MySQL 数据库中使用最为频繁的索引类型,除了 Archive 存储引擎之外的其他所有的存储引擎都支持 B-Tree 索引

  • Hash索引

  • Fulltext索引

  • R-Tree索引

B-树、B+树概念

B树

即二叉搜索树

在这里插入图片描述

特点:

  1. 所有非叶子结点至多有两个儿子(Left和Right)
  2. 所有结点存储一个关键字
  3. 非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树
B-树

是一种多路搜索树(并不是二叉的)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WygzEvX2-1621440867199)(E:/typora/B-Tree.png)]

  1. 定义任意非叶子结点最多只有M个儿子,且M > 2
  2. 跟结点的儿子数为 [2, M]
  3. 除跟结点以外的非叶子结点的儿子数为[M / 2, M]
  4. 每个结点存放至少M / 2 – 1(取上整)和至多M – 1个关键字(至少两个关键字)
  5. =

    1

    非叶子结点的关键字个数 \quad = \quad 指向儿子的指针个数 \quad – \quad 1

    =1

  6. 非叶子结点的关键字:K[1], K[2], …, K[M – 1],且K[i] < K[i – 1]
  7. 非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;
  8. 所有叶子结点位于同一层;

特点:

  1. 关键字集合分布在整棵树中
  2. 任何一个关键字出现且只出现在一个结点中
  3. 搜索有可能在非叶子结点结束
  4. 其搜索虚拟等价于在关键字全集内做一次二分查找
  5. 自动层次控制
B+树

B+树是B-树的变体

  1. 其定义基本与B-树同,除了:
  2. 非叶子结点的子树指针与关键字个数相同;
  3. 非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间);
  4. 为所有叶子结点增加一个链指针;
  5. 所有关键字都在叶子结点出现;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lrrtQ7ga-1621440867201)(E:/typora/B+Tree.png)]

特点:

  1. 所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的
  2. 不可能在非叶子结点命中
  3. 非叶子结点相当于叶子结点的索引(稀疏索引),叶子结点相当于存储(关键字)数据的数据层
  4. 更适合文件索引系统

建索引的几大原则

点击跳转到MySQL建索引的几大原则

线程池的好处

线程池的重用

线程的创建和销毁的开销是巨大的,而通过线程池的重用大大减少了这些不必要的开销,既然减少了这么多浪费内存的开销,其线程执行速度也是突飞猛进的提升

控制线程池的并发数

并发:在某个时间段内,多个程序都处在执行和执行完毕之间,但在一个时间点上只有一个抽象在运行

并行:在某个时间段里,每个程序按照自己独立异步的速度执行,程序之间互不干扰

控制线程池的并发数可以有效的避免大量的线程池争夺CPU资源而造成阻塞

详解

ThreadPoolExecutor

public ThreadPoolExecutor(
	int corePoolSize,
    int maximumPoolSize,
    long keepAliveTime,
    TimeUnit unit,
    BlockingQueue<Runnable> workQueue,
    ThreadFactory threadFactory,
    RejectedExecutorHandler handler
);
  • corePoolSize:线程池中核心线程的数量

  • maximumPoolSize:线程池中最大线程数量

  • keepAliveTime:非核心线程的超时时长,当系统中非核心线程闲置时间超过keepAliveTime之后,则会被回收。如果ThreadPoolExecutor的allowCoreThreadTimeOut设置为true,则该参数也表示核心线程的超时时长

  • unit:第三个参数单位,有纳秒、微妙、毫秒、秒、分、时、天等

  • workQueue:线程池中的任务队列,该队列主要用来存储已经被提交但尚未执行的任务。存储在这里的任务是由ThreadPoolExecutor的execute方法提交来的

  • threadFactory:为线程池通过创建新线程的功能,一般使用默认即可

  • handler:拒绝策略,当线程无法执行新任务时(一般是由于线程池中的线程数量已经达到最大数或者当线程池关闭导致的),默认情况下,当线程池无法策略新线程时,会抛出一个RejectExecutionException

后面两个参数基本不用

m

a

x

i

m

u

m

P

o

o

l

S

i

z

e

=

c

o

r

e

P

o

o

l

S

i

z

e

+

n

o

C

o

r

e

P

o

o

l

S

i

z

e

maximumPoolSize \quad = \quad corePoolSize \quad + \quad noCorePoolSize

maximumPoolSize=corePoolSize+noCorePoolSize

  1. 当currentSize < corePoolSize时,直接启动一个核心线程并执行任务
  2. 当currentSize >= corePoolSize并且workQueue未满时,添加进来的任务会被安排到workQueue中等待执行
  3. 当workQueue已满,但是currentSize < corePoolSize时,会连接开启一个非核心线程来执行任务
  4. 当currentSize >= corePoolSize、workQueue已满、并且currentSize < maximumPoolSize时,调用handler默认抛出一个RejectExecutorException异常

其它线程池

  • FixedThreadPool:有固定数量线程的线程池。其corePoolSize = maximumPoolSize,且keepAliveTime为0,适合线程稳定的场所
  • SingleThreadPool:有固定数量线程的线程池。其corePoolSize = maximumPoolSize = 1,且keepAliveTime为0,适合线程同步操作的场所
  • CachedThreadPool:既然要储存,其容量肯定很大,所以corePoolSize = 0,maximumPoolSize = Integer.MAX_VALUE(232-1)
  • ScheduledThreadPool:具有定时定期执行任务功能的线程池

线程池的单例

单例模式

单例模式(Singleton Pattern)是Java中最简单的设计模式之一。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建,这个类提供了一种访问其唯一的对象方式,可以直接访问,不需要实例化该类的对象

注意:

  • 单例类只能有一个实例
  • 单例类必须自己创建自己的唯一实例
  • 单例类必须给所有其他对象通过这一实例

线程池的单例

首先在ThreadPool类里面实现线程池的创建

然后对ThreadPool内部类,在类里面对它实例化,实现单例

Redis的过期删除策略

常见的三删除策略

  • 定时删除:在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作
  • 惰性删除:放任过期键不管,每次从键空间中获取值时,如果过期,则删除该键,如果没有过期,则返回该键
  • 定期删除:每隔一段时间,程序对数据库进行一次检查,删除里面的过期键

其中定时删除和定期删除为主动删除策略,惰性删除为被动删除策略

定时删除策略

定时删除策略通过使用定时器,定时删除策略可以保证过期键尽可能快的被删除,并释放过期键所占的内存

  • 优点:对内存非常友好
  • 缺点:对CPU时间非常不友好

惰性删除策略

惰性删除策略只会在获取键时才对键进行过期检查,不会在删除其它无关的过期键花费过多的CPU时间

  • 优点:对CPU时间非常友好
  • 缺点:对内存非常不友好

定期删除策略

定期删除策略每隔一段时间执行一次删除过期操作,并通过限制删除操作执行的时长和频率来减少删除操作对CPU时间的影响,同时,通过定期删除过期键,也有效减少了因为过期键带来的内存浪费

Redis使用的过期键删除策略

Redis服务器使用的是惰性删除策略和定期删除的实现

惰性删除策略的实现

过期键的惰性删除策略由expirelfNeeded函数实现,所有读写数据库的Redis命令在执行之前都会调用expirelfNeeded函数对输入键进行检查:

  • 如果输入键已过期,那么将输入键从数据库中删除
  • 如果输入键未过期,那么不做任何处理

在这里插入图片描述

定期删除策略的实现

过期键的定期删除处理由activeExpireCycle函数实现,每当Redis服务器的周期性操作serverCron函数执行时,activeExpireCycle函数就会被调用,它在锁定的时间里,分多次遍历服务器中的各个数据库,从数据库的expires字典中随机检查一部分键的过期时间,并删除其中的过期键

activeExpireCycle函数的执行流程:

函数每次运行时,都从一定数量的数据库中随机取出一定数量的键进行检查,并删除其中的键,比如先从0号数据库开始检查,下次函数运行时,可能就是从1号数据库开始检查,直到15号数据库检查完毕,又重新从0号数据库开始检查,这样可以保证每个数据库都被检查到

SQL优化

基本概念

逻辑架构

在这里插入图片描述

  • 第一层:客户端通过连接服务,将要执行的sql指令传输过来
  • 第二层:服务器解析并优化sql,生成最终的执行计划并执行
  • 第三层:存储引擎,负责数据的储存和提取

数据库通过锁机制来解决并发场景-共享锁(读锁)和排他锁(写锁)。读锁是不会阻塞的,多个客户端可以在同一时刻读取同一资源。写锁是排他的,并且会阻塞其他的读锁和写锁

  • 乐观锁,通常用于数据竞争不激烈的场景,多读少写,通过版本号和时间戳实现
  • 悲观锁,通常用于数据竞争激烈的场景,每次操作都会锁定数据

要锁定数据需要一定的锁处理来配合

  • 表锁,锁定整张表,开销最小,但是会加剧锁竞争
  • 行锁,锁定行级别,开销最大,但是可以最大程度的支持并发

但是MySQL的存储引擎的真实实现不是简单的行级锁,一般都是实现了多版本并发控制(MVCC),MVCC是行级锁的变种,开销更低。MVCC是通过保存数据的某个时间点快照实现的

事务

事务保证一组原子性的操作,要么全部成功,要么全部失败,一旦失败,回滚之前的所有操作。

MySQL采用自动提交,如果不是显示的开启一个事务,则某个查询都作为一个事务

隔离级别控制了一个事务值的修改,哪些事务内和事务间是可见的

四种常见的隔离级别:

  • 读未提交(Read UnCommitted),事务中的修改,即使没提交对其它事务也是可见的。事务可能读取未提交的数据,造成脏读
  • 读已提交(Read Committed),一个事务开始是,只能空间已提交的事务所做的修改。读未提交之前,所做的修改对其它事务是不可见的,也叫不可重复读,同一个事务多次读取同样的记录可能不同
  • 可重复读(RepeatTable Read),同一个事务中多次读取同样的记录结果时结果相同
  • 可串行化(Serializable),最高隔离级别,强制事务串行执行

存储引擎

InnoDB引擎,最重要,使用最广泛的存储引擎,被用来设计处理大量短期事务,具有高性能和自动崩溃恢复的特性

MyISAM引擎,不支持事务和行级锁,崩溃后无法安全恢复

创建时优化

Shema和数据类型优化

  • 尽量使用对应的数据类型
  • 选择更小的数据类型
  • 标识列尽量用整型
  • 不推荐ORM系统自动生成的Schema,通常具有不注重数据类型,使用很大的varchar类型,索引利用不合理等问题
  • 真实场景混用范式和反范式。冗余高查询效率高,插入更新效率低,冗余低插入更新效率高,查询效率低
  • 创建完全的独立的汇总表\缓存表,定时生成数据,用于用户耗时时间唱歌的操作
  • 表升级的过程中可以使用影子表的方式,通过修改原表的表名,达到保存历史数据,同时不影响新表使用的目的

索引

索引包含一个或多个列的值。MySQL只能高效的利用索引的最左前缀列

优势

  • 减少查询扫描的数据量
  • 避免排序和零时表
  • 将随机IO变为顺序IO

B-Tree索引

使用最多的索引类型。采用B-Tree数据结构来数据结构(每个叶子节点都包含指向下一个叶子节点的指针,从而方便叶子结点的遍历)。B-Tree索引适用于全键值,键值范围,键前缀查找,支持排序

B-Tree索引限制:

  • 如果不是按照索引的最左列开始查询,则无法使用索引
  • 不能跳过索引中的列
  • 如果查询中有个范围查询,则其右边的所有列都无法使用索引优化查询

哈希索引

只有精确匹配索引的所有类,查询才有效。存储引擎对所有的索引列计算一个哈希码,哈希索引将所有的哈希码存储在索引中,并保存指向每个数据行的指针

限制

  • 无法用于排序
  • 不支持部分匹配
  • 只支持等值查询

优化建议点

  • 注意每种索引的适用范围和适用限制
  • 索引列如果是表达式的一部分或者是函数的参数,则失效
  • 针对特别长的字符串,可以使用前缀索引,根据索引的选择性选择合适的前缀长度
  • 使用多列索引的时候,可以通过and 和 or 语法连接
  • 重复索引没必要
  • 索引在where条件查询和group by 语法查询时特别有效
  • 将范围查询放在条件查询的最后,防止范围查询导致的右边索引失效的问题
  • 索引最好不要选择过长的字符串,而且索引列也不宜为null

查询时优化

查询质量的三个重要指标

  • 响应时间(服务时间,排队时间)
  • 扫描的行
  • 返回的行

查询优化点

  • 避免查询无关的列
  • 避免查询无关的行
  • 切分查询。将一个对服务器压力较大的任务,分解到一个较长的时间中,并分多次执行
  • 分解关联查询。将多表关联查询的一次查询,分解成对单表的多次查询
  • 注意count的操作只能统计不为null的列,所以统计总的行数使用count(*)
  • group by 按照列标识分组效率高,分组结果不易出行分组列之外的列
  • 管理程序调用延迟管理,可以根据查询条件先缩小各自要查询的范围,再关联
  • limit分页优化。可以根据索引覆盖什么,再根据索引列关联自身查询其它的列
select id, name, age where student s1 inner join (
	select id where student order by age limit 50, 5
) as s2 on s1.id = s2.id;
  • union查询默认去重,如果不是业务必须,建议使用效率更高的union all

补充内容

  1. 条件中的字段类型和表结构类型不一致,MySQL会自动加转换函数,导致索引作为函数中的参数失效
  2. like查询前面部分未输入,以%开头无法命中索引
  3. 支持json格式数据并提供相关内置函数
  4. 关注explain在性能分析中的使用
    • select_type:有几个值,simple(表示简单的select,没有union和子查询),primary(有子查询,最外面的select查询就是primary),union(union中的第二个或随后的select查询,不依赖外部查询结果),dependent union(union中的第二个或随后的select查询,依赖外部查询结果)
    • type:有几种值:system(表仅有一行(=系统表),这是const连接类型的一个特例),const(常量查询), ref(非唯一索引访问,只有普通索引),eq_ref(使用唯一索引或组件查询),all(全表查询),index(根据索引查询全表),range(范围查询)
    • possible_keys: 表中可能帮助查询的索引
    • key:选择使用的索引
    • key_len:使用的索引长度
    • rows:扫描的行数,越大越不好
    • extra:有几种值:Only index(信息从索引中检索出,比扫描表快),where used(使用where限制),Using filesort (可能在内存或磁盘排序),Using temporary(对查询结果排序时使用临时表)

Spring用了哪些设计模式?

策略模式

本质上讲,策略模式就是一个接口下有多个实现类,而每一种实现类会处理某一种情况

注意

  • 使用@Component注解对当前类进行标注,将其声明为Spring容器所管理的一个bean
  • 声明一个返回boolean值的方法,通过这个方法来控制当前实例是否为处理目标request的实例
  • 声明一个方法用于处理业务逻辑,当然根据各个业务的不同声明的方法名肯定是不同的,这里只是一个对统一的业务处理的抽象
  • 这两个方法都需要传一个对象进行,而不是简简单单的基本类型的变量,这样做的好处后续如果要在Request中新增自动,那么就不需要修改接口的定义和已经实现的各个子类的逻辑

工厂方法模式

所谓的工厂方法模式,就是定义一个工厂方法,通过传入的参数,返回某个实例,然后通过该实例来处理后续的业务逻辑。一般的,工厂方法的返回值类型是一个接口类型,而选择具体子类实例的逻辑则封装到了工厂方法中了。通过这种方式,来将外层调用逻辑与具体的子类的获取逻辑进行分离

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/141936.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!