ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

java八股文复习

2021-09-22 22:03:05  阅读:170  来源: 互联网

标签:事务 缓存 八股文 复习 数据 查询 线程 java 方法


项目介绍

背景:项目是支付宝小程序“移动双V会员”开发,用户数量5000万,月活60万。

任务:负责四个项目:小程序后台需求迭代、抽奖模块活动迭代、内部数据查询web后台开发、支付宝客满平台维护

行动:MQ异步削峰处理每月十万级订购请求,对首页接口的接口网管限流处理,与支付宝进行SHA-256加密的用户数据查询服务,利用redis储存抽奖次数避免超发

成果:完成中移订购流程解偶,优化产品订购体验提升30%订购留存率。优化首页接口,将订购数据查询延迟从2s优化到30ms。数据库5s超时导致耗时5.5s近30万条代执行任务超时异常,优化慢查询sql将耗时时间优化到1.5s。

基础

servlet生命周期

实例化:在第一次访问或启动tomcat时,tomcat会调用此无参构造方法实例化servlet。(一次)
初始化:tomcat在实例化此 servlet 后,会立即调用 init() 方法初始化servlet。(一次)
就绪:容器收到请求后调用 servlet 的 service() 方法来处理请求。(多次)
销毁:容器删除 servlet 对象,删除前会调用 destory() 方法。(一次)

请求转发和重定向

请求转发:forward(); 重定向:sendRedirect()

重定向

1、使用 response 对象的 sendRedirect() 方法

2、setStatus() 和 setHeader() 方法一起使用

String site = “https://www.baidu.com/” ;

response.setStatus(response.SC_MOVED_TEMPORARILY);

response.setHeader(“Location”, site);

过滤器

spring boot通过FilterRegistrationBean实例注册,实现Filter类重写doFilter方法,对参数进行校验。创建一个FilterConfiguration类,允许的网站域名,限制 HEADER 或 METHOD,设置顺序

监听器

利用Listener实现Redis的缓存预热,实现ServletContextListener接口,重写contextInitialized()方法,获取service对象bean,获取mySql数据库中所有的User,将User缓存到Redis数据库中

JDK

hashcode

hashCode是jdk根据对象的地址算出来的一个int数字,代表对象在内存中的存储位置。重写equals方法的同时必须也要重写hashCode方法。

hash冲突:两个对象对应一个hashCode,但equals却并不相等,链表数据结构来解决hash冲突的情况

HashMap:是一个用于存储Key-Value键值对的集合,常使用的是两个方法:GetPut

put:哈希取模,头插法插入重复元素 get:做Hash映射,得到对应的index,重复元素尾出

HashSet

JVM

虚拟机

程序计数器:线程独立,改变计数器来选取指令

本地方法栈:Java 通过 JNI 直接调用本地 C/C++ 库

虚拟机栈(java栈):线程私有,存储局部变量表、操作数栈、动态链接、方法出口。

Java堆:Java堆是垃圾收集器管理的主要区域。线程共享。-Xmx4000m -Xms4000m LargePageSizeInBytes(大分页内存)=128m,使用70%后开始CMS收集

方法区:所有的①类(class)②静态变量(static变量)③静态方法④常量 ⑤成员方法

类加载机制

1、加载:生成二进制字节流,转化为方法区数据结果,生成对象

2、验证:对文件格式、元数据、字节码、引用验证

3、准备:给类分配内存设置初始值

4、解析:常量池的符号引用改为直接引用

5、初始化:执行代码

6、使用:使用阶段包括主动引用和被动引用,主动饮用会引起类的初始化,而被动引用不会引起类的初始化

7、卸载:类所有的实例都已经被回收、ClassLoader被回收、无反射(Major GC)

双亲委派模型

两种类加载器:

  • 启动类加载器(Bootstrap ClassLoader)

  • 其它类的加载器:

    扩展类加载器(Extension ClassLoader)
    应用程序类加载器(Application ClassLoader)
    自定义类加载器(User ClassLoader)

破坏双亲委派模型

1、继承ClassLoader覆盖loadClass() 2、线程上下文类加载器 3、热替换、模块热部署

为什么委托机制:递归的向父类查找,防止内存中出现多份同样的字节码

垃圾回收算法

原理:

引用计数算法:有一个地方引用它时,计数器值就加1。缺点:互相引用

根可达算法:GC Roots 的对象作为根,走过的路径称为引用链,没有引用链对象叫垃圾

垃圾收集算法

标记-清除算法、复制算法、标记-整理算法、分代收集算法

垃圾回收机制

Major GC:方法区GC,类加载机制的卸载

Minor GC:新生代“消失”的过程

新生代空间的构成:一个伊甸园空间(Eden)、两个幸存者空间(Fron Survivor、To Survivor)

Eden满了,执行Minor GC,移动到Fron Survivor空间,Survivor满了,存活对象移动到To Survivor,重复15次,剩下移动到老年代

老年代:Major GC 或者 Full GC,调优主要是减少 Full GC 的触发次数

CMS

用两次短暂的暂停来代替串行或并行标记整理算法时候的长暂停。使用 标记-清理 算法

1、初始标记:停顿线程STW,标记对象

2、并发标记:用户线程可以和GC线程一起并发执行

3、并发预清理:重新扫描,找到2并发标记过程需要被回收的

4、重新标记:停顿线程STW

5、并发清理:并发执行

6、并发重置:重置CMS收集器的数据

G1

三色标记法:白:所有对象 灰:全局变量和函数栈的对象,1、把灰对象全部置为黑 2、把原先灰对象指向的变量都置为灰色 3、等没有对象可以被置为灰色时,白色都是需要清除的

并行与并发的标记-复制垃圾回收器。G1将内存划分一个个固定大小的region,每个region可以是年轻代、老年代的一个。

整个堆被划分成2048左右个Region。每个Region的大小在1-32MB之间

Region的状态有三种:young(新生代)old(老年代)Humongous(半满)

把一个对象分配到Region内,只需要简单增加top的值,撞点到top值,就从新变老,维护一个空间Region的链表,回收之后的Region都会被加入到这个链表中。

ZGC

ZGC关键技术:着色指针和读屏障技术

JVM监控命令

jps : 虚拟机进程状况工具,列出正在运行的虚拟机进程

jinfo : Java配置信息工具

jmap : 生成堆转储快照

jstack :生成虚拟机当前时刻的线程快照

JConsole:常用工具

并发

多线程

创建线程的方法

1、继承Thread类,重写run()方法

2、实现Runnable接口,重写run()接口。

3、使用匿名内部类实现线程。new Thread() {run()}.start(); 相当于继承了Thread类,作为子类重写run()实现。

new Thread(new Runnable() {run()}).start(); 实现Runnable,Runnable作为匿名内部类

4、实现Callable接口,重写call(),带返回值,将FutureTask作为返回的承接对象

5、线程池的实现

Executor接口定义execute方法,接收一个Runable实例,执行任务,任务即一个实现了Runnable接口的类。ExecutorService接口继承自Executor接口,比如,ExecutorService提供关闭方法shutdown,以及跟踪任务执行状况的Future 方法。

6、定时器(java.util.Timer) //Timer 时间一般不够精确
timer.schedule(new TimerTask() {
@Override
public void run() {} }, 0, 1000);

7、Lambda表达式的实现(parallelStream)

parallelStream并行流利用多线程进行,并行操作共享变量没加锁,会导致流处理结果异常

//流打印
list.parallelStream().forEach(System.out::println);
//流计算求和
return list.parallelStream().mapToInt(i -> i).sum();

8、Spring实现多线程

 * step1、新建一个java配置类(注意需要开启@EnableAsync注解——支持异步任务)
 * step2、书写异步执行的方法类(注意方法上需要有@Async——异步方法调用)
 * step3、创建运行类

线程的状态

1、新建状态(new):使用 new 创建一个线程,仅仅只是在堆中分配了内存空间

2、可运行状态(runnable):新建状态调用 start() 方法,进入可运行状态。而这个又分成两种状态,ready和 running

​ 就绪状态(runnable):线程对象调用了 start() 方法,等待 JVM 的调度,(此时该线程并没有运行)

​ 运行状态(running):线程对象获得 JVM 调度,如果存在多个 CPU,那么运行多个线程并行运行

注意:线程对象只能调用一次 start() 方法,否则报错:illegaThreadStateExecptiong

3、阻塞状态(blocked):正在运行的线程因为某种原因放弃 CPU,暂时停止运行,就会进入blocked。此时 JVM 不会给线程分配 CPU, 直到线程重新进入ready,才有机会转到running。

4、等待状态(waiting):等待状态只能被其他线程唤醒,此时使用的是无参数的 wait() 方法

5、计时等待(timed waiting):调用了带参数的 wait(long time)或 sleep(long time) 方法

6、终止状态(terminated):通常称为死亡状态,表示线程终止

线程的方法

1、start():开启一个新的线程来执行用户定义的子任务

2、run():继承Thread类重写run方法,执行任务

3、sleep(long millis):线程休眠,不会释放同步锁。

4、wait():执行到wait(),就释放当前的锁,必须在同步代码块或同步方法中

5、notify()/notifyAll():唤醒wait的一个或所有的线程,会放弃同步锁,需和wait()成对使用,必须在同步代码块或同步方法中

6、join():联合线程:表示这个线程等待另一个线程完成后(死亡)才执行,join 方法被调用之后,线程对象处于阻塞状态。写在哪个线程中,哪个线程阻塞

7、yield():礼让线程,让出 CPU 资源,线程对象进入就绪状态,会优先给更高优先级的线程运行机会

线程的同步

1、同步方法:有synchronized关键字修饰的方法。

2、同步代码块:锁

3、特殊域变量(volatile)

​ a.volatile关键字为域变量的访问提供了一种免锁机制,
b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新
​ c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
​ d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量

4、使用重入锁实现线程同步

ReentrantLock类是可重入、互斥、实现了Lock接口的锁。ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用

5、使用局部变量实现线程同步

使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。

ThreadLocal() : 创建一个线程本地变量
get() : 返回此线程局部变量的当前线程副本中的值
initialValue() : 返回此线程局部变量的当前线程的"初始值"
set(T value) : 将此线程局部变量的当前线程副本中的值设置为value

ThreadLocal与同步机制区别
a.ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题。
b.前者采用以"空间换时间"的方法,后者采用以"时间换空间"的方式

6、使用阻塞队列实现线程同步

LinkedBlockingQueue是一个基于已连接节点的,范围任意的blocking queue。队列是先进先出的顺序(FIFO)

LinkedBlockingQueue 类常用方法
LinkedBlockingQueue() : 创建一个容量为Integer.MAX_VALUE的LinkedBlockingQueue
put(E e) : 在队尾添加一个元素,如果队列满则阻塞
size() : 返回队列中的元素个数
take() : 移除并返回队头元素,如果队列空则阻塞

组件

1、zk

zk在分布式系统中,对应用提供一致性保证,分布式选主,通过各种机制,对应用进行协调,从而使分布式系统对外提供某种特定的服务。

对于较低版本的zookeeper服务端,如3.4.x,则需要依赖curator2.x版本,如:2.12.0。如果使用高版本的curator,需要将curator自身依赖的ZooKeeper在maven中exclude掉。 并引入对应的低版本zookeeper客户端。

zk是通过使用curator实现的mutex锁(互斥锁)进行leader竞争。如果获取到的锁就是leader。
如果竞争leader的时候竞争锁失败,则会阻塞,并为上个节点添加watcher。

Curator提供两种方式进行集群选主,分别为:

  • LeaderLatch方式
  • LeaderElection方式

**LeaderLatch **通过 leaderLatch.start() 开启leader选举之后,我们需要调用 leaderLatch.await() 。如果当前的客户端未成为leader,则会进行等待,(内部源码实现是通过Object.wait()进行阻塞) 直到成为leader后,当前客户端线程会被唤醒,成为主节点就会以主节点的身份对外提供文件相关服务,其他非主节点阻塞。

LeaderElection需要建立一个LeaderSelector实例,它接收CuratorFramework实例、选举节点、以及一个LeaderSelectorListener Leader选举监听器。然后实现LeaderSelectorListener的回调方法:

  • takeLeadership回调中需要开发者实现当成为leader之后的业务逻辑。当一个客户端成为leader之后,便会回调takeLeadership方法,执行leader角色的业务逻辑
  • stateChanged方法需要开发者实现当连接状态发生变化之后的业务逻辑。比如:我们可以直接抛出异常,阻止leader业务逻辑继续进行。待另外的节点成为leader后执行takeLeadership方法

``

LeaderSelector leaderSelector = new LeaderSelector(
        client,
        election,
        new LeaderSelectorListener() {
            @Override
            public void takeLeadership(CuratorFramework curatorFramework) throws Exception {
                System.out.println("你已经成为leader");
                // 在 这里干leader的所有事情,此时方法不能退出
                Thread.sleep(Integer.MAX_VALUE);
            }

            @Override
            public void stateChanged(CuratorFramework curatorFramework, ConnectionState connectionState) {
                System.out.println("你已经不是leader,链接状态发生变化,connectionState" + connectionState);
                if (connectionState.equals(ConnectionState.LOST)) {
                    throw new CancelLeadershipException();
                }
            }
        });

数据库

数据库三范式

1.第一范式(1NF):列不可再分。地址分割为省、市、区
2.第二范式(2NF):属性完全依赖于主键。将订单编号和商品编号作为数据库表的联合主键
3.第三范式(3NF):属性不依赖于其它非主属性 属性直接依赖于主键。不可以在订单表中添加关于客户其它信息

B+Tree

MySQL使用的B+Tree结构都在经典B+Tree的基础上进行了优化,在B+Tree的每个叶子节点增加一个指向相邻叶子节点的顺序访问指针,优化了区间访问的性能。

B+树特征:

元素不保存数据,只用来索引,所有数据都保存在叶子节点。

所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。

为什么选择:

B树为了减少查找过程中磁盘I/O的存取次数,将一个存储节点大小设为等于一页,每个节点只需要一次I/O就可以完全载入。B树查询时间复杂度不定,最优为1次I/O,最差为O(log_dN)次。B+树二分查询的过程在内存中进行,利用节点间指针进行顺序索引减少I/O次数

SQL优化

三星索引:1、where查询条件的索引行相邻 2、order by排序与索引顺序一致 3、索引行包含所有列,避免回表操作

索引优化:

1、最左前缀原理:mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序调整。
2、选择区分度高的列作为索引,表示字段不重复的比例,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度是0

3、索引列不能参与计算,保持列“干净”

4、尽量的扩展索引,不要新建索引,表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。

查询优化:

避免慢SQL影响我们的性能

1、我们一定要避免在索引上使用计算。采用exists要比in效率高,因为IN不走索引

2、使用预编译查询。第一次执行时DBMS会为这个SQL语句进行查询优化并且执行预编译,以后直接使用预编译结果

3、尽量将多条SQL语句压缩到一句SQL中

4、用where字句替换HAVING字句,少用DISTINCT。HAVING只在检索出所有记录之后才对结果集进行过滤,而where则是在聚合前刷选记录

5、使用表的别名

MySQL的MVCC

ACID,分别为隔离性、一致性、原子性、持久性。在InnoDB中,利用日志恢复技术保证了事务的原子性和持久性,利用并发控制技术,保证了事务之间的隔离性。

事务的隔离级别

  • 读未提交(READ UNCOMMITTED) 脏读,读到未提交的数据
  • 读已提交(READ COMMITTED) 不可重复读 事务未提交,可能多次读取数据不一致
  • 可重复读(REPEATABLE READ) 发生幻读 读到别人提交的记录
  • 串行化(SERIALIZABLE) 事务串行

隔离级别导致的可能异常:

脏写:

两个事务同时在更新一条数据,更新之前这行数据的值为 NULL,主键为 XX。事务 A 是先更新A值,事务 B 更新完数据的值为 B,事务 A 突然回滚,就会用它的 undo log 日志去回滚,把那行数据的值更新回 NULL 值。

脏读:

事务 B 去查询事务 A 修改过的数据,A 还没提交,事务 A 随时会回滚导致 B 再次查询就读不到刚才事务 A 修改的数据

不可重复读:

事务 A 查询数据,读取到的就是 A 值。事务 B 更新那行数据的值为 B 值, 立马提交了,事务 A 此时还没提交。在事务执行期间第二次查询数据为B 值。接着事务 C 再次更新数据为 C 值,提交事务,此时事务 A 在还没提交的情况下,第三次查询数据,查到的值为 C 值

幻读:

幻读就是你一个事务用一样的 SQL 多次查询,结果每次查询都会发现查到一些之前没看到过的数据。

并发控制——锁

  • 按功能分:

    • 共享锁(读锁、S锁)
    • 排它锁(写锁、X锁)
  • 按锁粒度分:

    • 页锁
    • 表锁
    • 行锁

    表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;
    行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高;
    页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

多版本并发控制

MVCC手段只适用于Msyql隔离级别中的读已提交(Read committed)和可重复读(Repeatable Read)。

MVCC是通过保存数据在某个时间点的快照来进行控制的。使用MVCC就是允许同一个数据记录拥有多个不同的版本。然后在查询时通过添加相对应的约束条件,就可以获取用户想要的对应版本的数据。

MVCC过程:

InnoDB的MVCC,是通过在每行纪录后面保存两个隐藏的列来实现的。一个保存了行的创建时间,一个保存了行的过期时间

在REPEATABLE READ隔离级别下,MVCC具体的操作如下:

1、开始事务

2、记录数据行数据快照到undo log

3、更新数据

4、将undo log写到磁盘

5、将数据写到磁盘

6、提交事务

日志缓存

redo log(重做日志):包括重做日志缓冲(redo log buffer),日志易失;重做日志文件(redo log file),是持久的

记录的是每一个数据页中的更改

undo log(回滚日志):保存了事务发生之前的数据的一个版本,当事务提交之后,undo log 并不能立马被删除,而是放入待清理的链表,由 purge 线程判断是否有其它事务在使用 undo 段中表的上一个事务之前的版本信息。

binlog (二进制日志):存储着每条变更的SQL语句

分库分表

垂直拆分:通过用户的唯一uid做关联。将用户的相关数据拆分成为用户表、账户表、记录表、流水表等相关表

水平拆分:同一张表中的数据拆分到不同的数据库中进行存储、把一张表拆分成 n 多张小表。例如32库1024表格

读写分离:更新数据时,应用将数据写入master主库,主库将数据同步给多个slave从库。查询数据时,选择某个slave节点读取

分片策略:常用的方法有:HASH取模、范围分片、地理位置分片、时间分片等

全局ID:需要有一个全局的id生成器。比较轻量级的方案是,把一个64位的long型的id,1个bit是不用的,用其中的41 bit作为毫秒数,用10 bit作为工作机器id,12 bit作为序列号

分布式事务:柔性事务是目前比较主流的方案。最大努力通知型、可靠消息最终一致性方案以及TCC两阶段提交

Redis

单线程的 Redis 为什么这么快:

纯内存操作、单线程操作、非阻塞 I/O 多路复用机制

I/O 多路复用机制:小曲在 S 城开了一家快递店,负责同城快送服务。小曲因为资金限制,雇佣了一批快递员,然后小曲发现资金不够了,只够买一辆车送快递。

String:一个 key 对应一个 value,string 类型的值最大能存储 512MB。做一些复杂的计数功能

Hash:Redis hash 是一个键值(key=>value)对集合适合用于存储对象。做单点登录的时候,存储用户信息

List:简单的字符串列表,按照顺序插入。简单的消息队列、基于 Redis 的分页功能

Set:是 string 类型的无序集合,集合是通过哈希表实现的 做全局去重

zset:也是string类型元素的集合,且不允许重复的成员,每个元素都会关联一个double类型的分数,通过分数来为集合中的成员进行从小到大的排序 排行榜功能

Stream:主要用于消息队列,缺点就是消息无法持久化

内存淘汰机制

**allkeys-lru:**当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 Key

**volatile-lru:**当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 Key

Redis 和数据库双写一致性问题

首先,采取正确更新策略,先更新数据库,再删缓存。其次,因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。

持久化机制

RDB机制(异步):save, shutdown, slave 命令会触发一个操作,父进程执行fork操作创建子进程,遍历hash table,子进程创建RDB数据文件,fork() 可能会非常耗时,会丢失数据

AOF机制:持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录

集群策略

主从+哨兵模式、Cluster集群

内存淘汰算法

LRU(最近最少使用)、LFU(最不经常使用页置换算法)

缓存失效

1、大面积的缓存key失效:使redis的失效时间在一段时间内随机分布,利用LRU/LFU算法,处理淘汰缓存

2、热点key失效:加锁重构缓存、缓存降级、做备份缓存

缓存穿透

业务层穿透redis直达数据库,导致数据I/O压力大增。

解决:利用互斥锁、采用异步更新策略(Key 是否取到值,都直接返回,缓存预热)、布隆过滤器作为拦截机制

缓存雪崩

给缓存的失效时间、使用互斥锁、双缓存(从缓存 A 读数据库,有则直接返回;A 没有数据,直接从 B 读数据,直接返回,并且异步启动一个更新线程,更新线程同时更新缓存 A 和缓存 B)

数据不一致问题

采用rehash自动漂移策略,在节点多次上下线之后,也会产生脏数据

缓存降级

可以砍掉某个功能,也可以砍掉某些模块。

缓存限流(热点key)

限流算法有:计数器、漏桶和令牌桶算法。

计数器:使用LinkedList来记录滑动窗口的10个格子,每次移动都需要记录当前服务请求的次数。当前访问次数和LinkedList中最后一个相差是否超过100,如果超过就需要限流了。

漏桶算法:使用队列来实现

令牌桶算法:存放固定容量令牌(token)的桶,按照固定速率往桶里添加令牌

标签:事务,缓存,八股文,复习,数据,查询,线程,java,方法
来源: https://blog.csdn.net/Leeand/article/details/120423845

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有