Java高级篇
目录1 新技术1.1 Java 81.2 Java 91.3 Java 101.4 Java 111.5 Spring 51.6 Spring Boot 2.01.7 HTTP/21.8 HTTP/32 性能优化2.1 使用单例2.2 使用 Future 模式2.3 使用线程池1 新技术1.1 Java 8lambda 表达式、Stream API、时间 API1.2 Java 9Jig...
1 新技术
1.1 Java 8
lambda 表达式、Stream API、时间 API
1.2 Java 9
Jigsaw、Jshell、Reactive Streams
1.3 Java 10
局部变量类型推断、G1 的并行 Full GC、ThreadLocal 握手机制
1.4 Java 11
ZGC、Epsilon、增强 var
1.5 Spring 5
响应式编程
1.6 Spring Boot 2.0
1.7 HTTP/2
1.8 HTTP/3
2 性能优化
2.1Java程序优化
2.1.1 使用单例
2.1.2 使用 Future 模式
Future模式是多线程中非常常见的一种设计模式,它的核心思想是异步调用。
当我们需要调用一个函数方式是,如果这个函数执行很慢,那么我们就要进行等待。但有时候,我们可能并不急着需要结果。,因此,我们可以让被调者立即返回,让他在后台慢慢处理这个请求。对于调用者来说,则可以先处理一些其他任务,在真正需要数据的场合再去尝试获得需要的数据。
2.1.3 使用线程池
-
什么是线程池
- 线程池中,总有那么几个活跃线程。当需要使用线程时,可以从池子中随便拿一个空闲线程,当完成工作时,并不急着关闭线程,而是将这个线程退回到池子,方便其他人使用。简而言之,在使用线程池后,创建线程变成了从线程池中获取空闲线程,关闭线程变成了向池子归还线程。 JDK对线程池的支持
-
为了能够更好地控制多线程,JDK提供了一套
Executor框架,帮助开发人员有效地进行线程控制,其本质就是一个线程池。
以上成员均在java.util.concurrent包中,是JDK并发包的核心类。其中ThreadPoolExecutor表示一个线程池。Executors类则扮演着线程池工厂的角色,通过Executors可以取得一个拥有特定功能的线程池。
主要工厂方法:
- newFixedThreadPool()
返回一个固定线程池数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有空闲线程时,便处理在任务队列中的任务。 - newSingleThreadExecutor()
该方法返回一个只有一个线程的线程池。若多余的一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。 - newCachedThreadPool()
该方法返回一个可根据实际情况调整线程数量的线程池。线程池的数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。 - newSingleThreadScheduledExecutor()
该方法返回一个ScheduledExecutorService对象,线程池大小为1。ScheduledExecutorService接口在ExecutorService接口之上扩展了在给定时间执行某任务的功能,如在某个固定的延时之后执行,或者周期性执行某个任务。 - newScheduledThreadPool()
该方法也返回一个ScheduledExecutorService对象,但该线程池可以指定线程数量。
-
线程池关键点
- 尽量减少线程切换和管理的开支;
- 最大化利用CPU;
2.1.4 选择就绪、减少上下文切换
-
上下文切换
上下文切换是指CPU的控制权由运行任务转移到另外一个就绪任务时所发生的事件;
让步式上下文切换:指执行线程主动释放CPU,与锁竞争眼中程度成正比,可通过减少锁竞争来避免。
抢占式上下文切换:指线程因分配的时间片用尽而被迫泛起CPU或者被其他优先级更高的线程所抢占,一般由于线程数大于CPU可用核心数引起,可通过调整线程数,适当减少线程数来避免。 -
减少上下文切换
无锁并发编程:多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁,如将数据的 ID 按照 Hash 算法取模分段,不同的线程处理不同段的数据;
CAS:避免加锁和线程阻塞;
使用最少线程:避免创建不需要的线程,比如任务很少,但是创建了很多线程来处理,这样会造成大量线程都处于等待状态;
协程:比线程更轻量级的并发机制;在单线程实现多任务的调度,并在单线程里维持多个任务间的切换。
2.1.5 锁的优化
- 减小锁持有时间
只在必要的时候进行同步,synchronized的作用对象优先级:变量>方法>类; - 减小锁粒度
一种削弱多线程锁竞争的有效手段。
如ConcurrentHashMap中的应用,它内部进一步细分了若干个小的HashMap,称之为段(SEGMENT)。默认情况下,一个ConcurrentHashMap被进一步细分为16个段。
在ConcurrentHashMap中增加一个新的表项,并不是将整个HashMap加锁,而是首先根据hashcode得到该表项应该被存放到哪个段中,然后对该段加锁,并完成put()操作。在多线程环境中,乳沟多个线程同时进行put()操作,只要被加入的表项不存放在同一个段中,则线程间便可以做到真正的并行。 - 读写分离锁来替换独占锁
减少锁粒度是通过分割数据结构实现的,读写锁则是对系统功能点的分割。
在读多写少的场合,使用读写锁可以有效提升系统的并发能力。 - 锁分离
读写锁根据读写操作功能上 的不同,进行了有效的锁分离。依据应用程序的功能特点,使用类似的分离思想,也可以对独占锁进行分离。一个经典案例:java.util.concurrent.LinkedBlockingQueue的实现。
LinkedBlockingQueue是基于链表的,从队列中取数take() 和增加数据put() 分别作用于队列的前端和尾端,理论上说,两者并不冲突。
使用独占锁,则要求两个操作进行时获取当前队列的独占锁,则take() 和put() 并不能真正的并发,在运行时,会彼此等待对方释放锁资源。
因此,在JDK的实现中,取而代之的是两把不同的锁,分离了take() 和 put() 操作。 - 锁粗化
虚拟机在遇到一连串地对同一锁不断进行请求和释放的操作时,便会把所有的锁操作整合成对锁的一次请求,从而减少对锁的请求同步次数。
详细参考:实战Java高并发程序设计第四章
2.2 数据压缩
对于行存储(相比列存储)的表和索引,启用数据压缩最直接效果是能够减小数据占用的存储空间的大小;除了节省空间之外,数据压缩还能提高 I/O 密集型查询的性能,因为数据存储在更少的数据页(Data Page)中,SQL Server需要从磁盘读取的数据页更少,数据从Disk加载到内存的速度更快,查询的性能更好。但是,压缩和解压缩的过程都需要消耗额外的CPU资源,开发者必须均衡CPU资源,数据存储和硬盘IO的开销。
详细介绍:数据压缩
2.3 结果缓存
3 线上问题分析
此章节待整理
3.1 dump 获取
线程 Dump、内存 Dump、gc 情况
3.2 dump 分析
分析死锁、分析内存泄露
3.3 dump 分析及获取工具
jstack、jstat、jmap、jhat、Arthas
3.4 自己编写各种 outofmemory,stackoverflow 程序
HeapOutOfMemory、 Young OutOfMemory、
MethodArea OutOfMemory、ConstantPool OutOfMemory、
DirectMemory OutOfMemory、Stack OutOfMemory Stack OverFlow
3.5 Arthas
jvm 相关、class/classloader 相关、monitor/watch/trace 相关、
options、管道、后台异步任务
文档:https://alibaba.github.io/arthas/advanced-use.html
3.6 常见问题解决思路
内存溢出、线程死锁、类加载冲突
3.7 使用工具尝试解决以下问题,并写下总结
当一个 Java 程序响应很慢时如何查找问题
当一个 Java 程序频繁 FullGC 时如何解决问题
如何查看垃圾回收日志
当一个 Java 应用发生 OutOfMemory 时该如何解决
如何判断是否出现死锁
如何判断是否存在内存泄露
使用 Arthas 快速排查 Spring Boot 应用404/401问题
使用 Arthas 排查线上应用日志打满问题
利用 Arthas 排查 Spring Boot 应用 NoSuchMethodError
4 编译原理知识
待整理
5 操作系统
待整理
6 数据库
6.1 MySQL 存储引擎
-
MyISAM
访问速度快,但不支持事务,也不支持外键;对事务完整性没有要求或者以SELECT、INSERT为主的应用基本都可以使用这个引擎。
曾经有位阿里面试官告诉我,MyISAM基本不会使用了,其功能都可以使用Memcached完成。 -
InnoDB
事务型存储引擎,MySQL默认的存储引擎。支持行级锁定和外键约束。
-
适合场景:
- 更新密集的表;InnoDB存储引擎特别适合处理多重并发的更新请求。
- 事务;InnoDB存储引擎是支持事务的标准MySQL存储引擎。
- 自动灾难恢复;与其它存储引擎不同,InnoDB表能够自动从灾难中恢复。
- 外键约束;MySQL支持外键的存储引擎只有InnoDB。
- 支持自动增加列AUTO_INCREMENT属性。
- MEMORY
使用MySQL Memory存储引擎的出发点是速度。为得到最快的响应时间,采用的逻辑存储介质是系统内存。虽然在内存中存储表数据确实会提供很高的性能,但当mysqld守护进程崩溃时,所有的Memory数据都会丢失。获得速度的同时也带来了一些缺陷。它要求存储在Memory数据表里的数据使用的是长度不变的格式,这意味着不能使用BLOB和TEXT这样的长度可变的数据类型,VARCHAR是一种长度可变的类型,但因为它在MySQL内部当做长度固定不变的CHAR类型,所以可以使用。
6.2 MySQL逻辑架构
MySQL逻辑架构分为三层
- 客户端
如,连接处理、授权认证、安全等功能 - 核心服务
MySQL大多数核心服务均在这一层,包括查询解析、分析、优化、缓存、内置函数(如,时间、数学、加密等),所有的跨存储引擎的功能也在这一层,如,存储过程、触发器、视图等 - 存储引擎
负责MySQL中的数据存储和读取
中间的服务层通过API与存储引擎通信,这些API屏蔽了不同存储引擎间的差异
-
查询缓存
- 对于select语句,在解析查询之前,服务器会先检查查询缓存(Query Cache)。如果命中,服务器便不再执行查询解析、优化和执行的过程,而是直接返回缓存中的结果集。
6.3 MySQL查询过程
MySQL查询过程:
- 客户端将查询发送到MySQL服务器;
- 服务器先检查查询缓存,如果命中,立即返回缓存中的结果;否则进入下一阶段;
- 服务器对SQL进行解析、预处理,再由优化器生成对象的执行计划;
- MySQL根据优化器生成的执行计划,调用存储引擎API来执行查询;
- 服务器将结果返回给客户端,同时缓存查询结果。
参考链接:MySQL执行计划解析
6.4 执行计划
-
什么是执行计划
-
简单的来说,就是SQL在数据库中执行时的表现情况,通常用于SQL性能分析、优化场景。
可以通过 explain关键字查看优化器优化过程中的各个因素,使用户知道数据库是如何进行优化决策的,并提供一个参考基准,便于用户重构查询和数据库表的schema、修改数据库配置等,使查询尽可能高效。
可通过**关键字提示(hint)**优化器,从而影响优化器的决策过程。
MySQL会解析查询,并创建内部数据结构(解析树),并对其进行各种优化,包括重写查询、决定表的读取顺序、选择合适的索引等。
6.4.1 EXPLAIN 输出列信息
Column | JSON Name | Meaning |
---|---|---|
id | select_id | 查询序列号 |
select_type | None | 查询类型 |
table | table_name | 输出行信息所属表 |
partitions | partitions | 匹配的分区 |
type | access_type | 连接使用类型 |
possible_keys | possible_keys | 可能加速查询的索引 |
key | key | 真正使用的索引 |
key_len | key_length | 使用的索引长度,在不损失精确性的情况下,长度越短越好 |
ref | ref | 索引所在的列 |
rows | rows | 引擎认为必须检查的用来返回请求数据的行数 |
filtered | filtered | 按表条件过滤的行百分比 |
Extra | None | 附加信息 |
参考:MySQL官方文档
亦可参考链接:MySQL执行计划解析
6.5 索引
6.5.1 Hash索引
哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快。
6.5.2 B树索引
-
B树
-
类似二叉搜索树,实质为一棵m阶B树是一棵平衡的m路搜索树;
最重要的性质是每个非根节点所包含的关键字个数 j 满足:┌m/2┐ - 1 <= j <= m - 1;一个节点的子节点数量会比关键字个数多1,这样关键字就变成了子节点的分割标志。一般会在图示中把关键字画到子节点中间,非常形象,也容易和后面的B+树区分。由于数据同时存在于叶子节点和非叶子结点中,无法简单完成按顺序遍历B树中的关键字,必须用中序遍历的方法。
-
B+树
-
一棵m阶B树是一棵平衡的m路搜索树;
最重要的性质是每个非根节点所包含的关键字个数 j 满足:┌m/2┐ - 1 <= j <= m;子树的个数最多可以与关键字一样多。非叶节点存储的是子树里最小的关键字。同时数据节点只存在于叶子节点中,且叶子节点间增加了横向的指针,这样顺序遍历所有数据将变得非常容易。
-
B*树
-
一棵m阶B树是一棵平衡的m路搜索树;
最重要的两个性质是:(1)每个非根节点所包含的关键字个数 j 满足:┌m2/3┐ - 1 <= j <= m;(2)非叶节点间添加了横向指针。
B+树适合作为数据库的基础结构的原因
完全是因为计算机的内存-机械硬盘两层存储结构。内存可以完成快速的随机访问(随机访问即给出任意一个地址,要求返回这个地址存储的数据)但是容量较小。而硬盘的随机访问要经过机械动作(1磁头移动 2盘片转动),访问效率比内存低几个数量级,但是硬盘容量较大。典型的数据库容量大大超过可用内存大小,这就决定了在B+树中检索一条数据很可能要借助几次磁盘IO操作来完成。
-
R数
- 详细介绍: 经典查找算法 — R树
6.5.3 聚簇索引与非聚簇索引
当数据库一条记录里包含多个字段时,一棵B+树就只能存储主键,如果检索的是非主键字段,则主键索引失去作用,又变成顺序查找了。这时应该在第二个要检索的列上建立第二套索引。 这个索引由独立的B+树来组织。
有两种常见的方法可以解决多个B+树访问同一套表数据的问题,一种叫做聚簇索引(clustered index ),一种叫做非聚簇索引(secondary index)。
-
InnoDB使用的是聚簇索引
将主键组织到一棵B+树中,而行数据就储存在叶子节点上,若使用"where id = 14"这样的条件查找主键,则按照B+树的检索算法即可查找到对应的叶节点,之后获得行数据。若对Name列进行条件搜索,则需要两个步骤:第一步在辅助索引B+树中检索Name,到达其叶子节点获取对应的主键。第二步使用主键在主索引B+树种再执行一次B+树检索操作,最终到达叶子节点即可获取整行数据。 -
MyISM使用的是非聚簇索引
非聚簇索引的两棵B+树看上去没什么不同,节点的结构完全一致只是存储的内容不同而已,主键索引B+树的节点存储了主键,辅助键索引B+树存储了辅助键。表数据存储在独立的地方,这两颗B+树的叶子节点都使用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别。由于索引树是独立的,通过辅助键检索无需访问主键的索引树。
以上参考链接:MySQL的InnoDB索引原理详解
6.5.4 普通索引、唯一索引
-
普通索引
- 普通索引的唯一任务是加快对数据的访问速度。普通索引允许索引列中包含重复值。 唯一索引
- 唯一索引可以保证数据记录的唯一性。唯一索引不允许索引列有重复值,唯一索引的优点:1)简化了MySQL对这个索引的管理工作,这个索引也因此而变得更有效率;2)MySQL会在有新记录插入数据表时,自动检查新记录的这个字段的值是否已经在某个记录的这个字段里出现过了;如果是,MySQL将拒绝插入那条新记录。 主键
- 主键可以保证记录的唯一和主键域非空,数据库管理系统对于主键自动生成唯一索引,所以主键也是一个特殊的索引。 主键和唯一索引区别
-
一个表中可以有多个唯一性索引,但只能有一个主键。
主键列不允许空值,而唯一性索引列允许空值。
6.5.5 覆盖索引
covering index 指的是一个查询语句的执行只用从索引中就能够获得查询结果,不必从数据表中读取,也可以称之为索引覆盖。如果一个索引包含了(或覆盖了)满足查询语句中字段与条件的数据就叫做覆盖索引。
当一条查询语句符合覆盖索引条件时,sql只需要通过索引就可以返回查询所需要的数据,这样避免了查到索引后再返回表操作,减少I/O提高效率。
使用覆盖索引Innodb比MyISAM效果更好----InnoDB使用聚集索引组织数据,如果二级索引中包含查询所需的数据,就不再需要在聚集索引中查找了。
遇到以下情况,执行计划不会选择覆盖查询:
- select选择的字段中含有不在索引中的字段 ,即索引没有覆盖全部的列。
- where条件中不能含有对索引进行like的操作。
6.5.6 最左前缀原则
在联合索引(多列索引)中,选择索引的列顺序有一个经验法则:将选择性最高的列放到索引最前列。
在一个多列B-Tree索引中,索引列的顺序以为着索引首先按照最左列进行排序,其次是第二列。
也就是说,当进行查找时,首先是最先匹配最左的条件,在匹配第二个。然而当查找条件不包含联合索引中其中索引列的任一左边的列时,此时查询都不会走索引。
例:
select * from test where a = ‘1’ and b = ‘2’ and c = ‘3’;
其中联合索引:index(a,b,c)
当查询过程中,查询条件只有b,c 时,不会命中联合索引;
只有a, c 时,亦不会命中;
6.5.7 Index Condition Pushdown 索引下推
Using Index Condition Pushdown, the scan proceeds like this instead:
Get the next row’s index tuple (but not the full table row).
Test the part of the WHERE condition that applies to this table and can be checked using only index columns. If the condition is not satisfied, proceed to the index tuple for the next row.
If the condition is satisfied, use the index tuple to locate and read the full table row.
Test the remaining part of the WHERE condition that applies to this table. Accept or reject the row based on the test result.
SELECT * FROM people
WHERE zipcode='95054'
AND lastname LIKE '%etrunia%'
AND address LIKE '%Main Street%';
当没有使用ICP时,MySQL服务器首先查询出满足"zipcode=‘95054’" 条件的所有数据,在内存中再筛选出"lastname LIKE ‘%etrunia%’"和"address LIKE ‘%Main Street%’"条件满足的数据返回给客户端。
使用了ICP条件时,直接在读出数据之前会筛选出符合后两个条件的数据,减少IO读操作,提高性能。
索引下推只能作用在辅助索引( secondary indexes)上;
对于InnoDB的聚簇索引,索引下推技术无效,其完整记录已经读入了InnoDB的缓冲区中,所以并不能减少IO操作。
6.6 SQL 优化
参考链接:sql优化的几种方式
6.7 数据库事务和隔离级别
-
事务
- 事务是访问数据库的一个操作序列,数据库应用系统通过事务集来完成对数据库的存取。事务的正确执行使得数据库从一种状态转换为另一种状态。 特性(ACID)
- 原子性 atomicity
- 一致性 consistency
- 隔离性 isolation
- 持久性 durability 事务的隔离级别
- 无锁
- 读锁
- 写锁
- 串行化
隔离级别参考链接:面试官:谈谈Mysql事务隔离级别?
6.8 数据库锁
- 表锁
开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。 - 行锁
开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。 - 页面锁
开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
详细介绍:MySQL中的锁(表锁、行锁)
7 数据结构
8 大数据知识
8.1 zookeeper
8.1.1 基本概念
ZooKeeper是一个开放源码的分布式协调服务,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
分布式应用程序可以基于Zookeeper实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能。
-
Zookeeper保证了如下分布式一致性特性
- 顺序一致性
- 原子性
- 单一视图
- 可靠性
- 实时性(最终一致性)
客户端的读请求可以被集群中的任意一台机器处理,如果读请求在节点上注册了监听器,这个监听器也是由所连接的zookeeper机器来处理。对于写请求,这些请求会同时发给其他zookeeper机器并且达成一致后,请求才会返回成功。因此,随着zookeeper的集群机器增多,读请求的吞吐会提高但是写请求的吞吐会下降。
有序性是zookeeper中非常重要的一个特性,所有的更新都是全局有序的,每个更新都有一个唯一的时间戳,这个时间戳称为zxid(Zookeeper Transaction Id)。而读请求只会相对于更新有序,也就是读请求的返回结果中会带有这个zookeeper最新的zxid。
更多推荐
所有评论(0)