
= = 比基本类型:比较值,
= = 比引用类型:比较引用。
equals本质上就是= =,String、integer等重写了equals方法,把它变成了值比较。
String 每次操作都会生成新的String对象
StringBuffer 线程安全的,所有公开方法都是 synchronized 修饰的
StringBuilder 非线程安全的,性能更高一些
前者会放到常量池,后者放到堆内存
ArraList 是基于数组实现的线性表,尾部插入和数据访问效率高
LinkedList是双向链表,中间插入和头部插入效率高,查询效率低

HashMap 的默认大小是16(16是为了确保算出来的值足够随机),
当容量到达阈值的时候会触发扩容,阈值=容量加载因子,160.75=12。
每次扩容是之前容量的2倍。
JDK 1.7:空参构造函数,内部是空数组,第一次put初始化数组。
JDK1.8 : 空参构造函数,内部是null,第一次put初始化数组。
1、判断键值对数组是否为空(null)或者length=0,是的话就执行resize()方法进行扩容。
2、不是就根据键值key计算hash值得到插入的数组索引i。
3、判断索引i这个位置是否是null,如果是,就新建节点。如果不是,判断首个元素是否和key一样,一样就直接覆盖。
4、如果位置I的首个元素和key不一样,判断是否是红黑树,如果是红黑树,直接在树中插入键值对。
5、如果不是红黑树,开始遍历链表,判断链表长度是否大于8,如果大于8就转成红黑树,在树中执行插入操作,如果不是大于8,就在链表中执行插入;在遍历过程中判断key是否存在,存在就直接覆盖对应的value值。
6、插入成功后,就需要判断实际存在的键值对数量size是否超过了最大容量threshold,如果超过了,执行resize方法进行扩容。
Jdk1.7

JDK1.8

JDK1.7,结构为segment+数组+链表,通过继承 ReentrantLock加分段锁。每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的。
JDK1.8,放弃了锁分段的概念,使用Node+CAS+Synchronized的方式实现并发,的结构为数组+链表+红黑树
DateFormat的所有实现都是线程不安全的。
java.util.Date表示一个日期和时间,通常用于表示一个时间戳。
java.sql.Date是java.util.Date的子类,它只表示日期,不包含时间。
不会,这个对象可能被回收
线性表、链表、栈、队列、map、树
编译时异常、运行时异常
编译时异常:空指针异常、数值转换异常、数组越界异常等
饿汉式:默认创建对象
懒汉式:第一次使用时创建对象
饿汉式:1、静态变量,2、静态代码块

懒汉式:3、静态变量(初始化方法加载),4、同步方法,5、同步代码块

6、双重检查

7、静态内部类(利用jvm对静态内部类在使用时加载的机制)

8、枚举
1、Vector (vector比arraylist多个synchronized同步,因此系统开销比arraylist大)
2、hashTable
3、ConcurrentHashMap
synchronized主要有三种使用方式:修饰普通同步方法、修饰静态同步方法、修饰同步方法块。
锁的升级过程:
1、无锁,不锁住资源,多个线程只有一个能修改资源成功,其他线程会重试;
2、偏向锁,同一个线程获取同步资源时,没有别人竞争时,去掉所有同步操作,相当于没锁;
3、轻量级锁,多个线程抢夺同步资源时,没有获得锁的线程使用CAS自旋等待锁的释放;
4、重量级锁,多个线程抢夺同步资源时,使用操作系统的互斥量进行同步,没有获得锁的线程阻塞等待唤醒
synchronized关键字三大特性:
原子性(指令不可分割)、
可见性(其他线程可看见修改)、
有序性(代码是顺序执行)
volatile关键字只能保证可见性和有序性,不能保证原子性,也称为是轻量级的synchronized。
悲观锁:每次访问共享资源时都会上锁。
非公平锁:线程获取锁的顺序并不一定是按照线程阻塞的顺序。
可重入锁:已经获取锁的线程可以再次获取锁。
独占锁或者排他锁:该锁只能被一个线程所持有,其他线程均被阻塞
List list = new ArrayList<>();
list.add("1");
Collection listC = Collections.unmodifiableCollection(list); // listC不可修改
listC.add("2"); // listC 报错

1、程序计数器:指向当前线程正在执行的字节码指令的行号。
2、本地方法栈:Native方法。
3、栈(虚拟机栈):每个Java方法在被调用的时候都会创建一个栈帧,用来存储局部变量表(八大原始类型、封装类型)、作数栈、动态链接、方法出口。
4、堆:对象实例、数组
5、方法区:各个线程共享的内存区域,存储类的结构信息,例如运行时常量池,字段(通过引用常量池中的常量来描述)和方法等数据,以及方法和构造函数的代码,包括用于类和实例初始化以及接口初始化的特殊方法。


标记-清除算法:
1、标记,从根节点出发遍历对象,对访问过的对象打上标记
2、清除,对没有标记的对象进行回收
标记-整理算法:
1、标记,从根节点出发遍历对象,对访问过的对象打上标记
2、让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
复制算法:用在新生代,把可用的对象从一个地方拷贝到另一个地方
三色标记法:CMS、G1垃圾回收器都使用了三色标记法。
白色:没有标记的对象(垃圾对象)
灰色:该对象已标记,对象下的属性还没全被标记
黑色:该对象已标记,对象下的属性也全被标记
分代回收算法:堆空间分为新生代和老年代
Minor GC : 新生代回收
Major GC : 老年代回收,出现了 Major GC,经常会伴随至少一次的 Minor GC。
Full GC : 新生代 + 老年代回收
可达性分析算法:以GC Roots为起始点,从上至下的方式搜索被根对象集合所连接的目标对象是否可达。内存中的存活对象都会被根对象集合直接或间接连接着,搜索所走过的路径称为引用链。
JDK1到JDK13的发展历程中,一共出现了10种垃圾回收器

各个收集器的Full GC:

栈中引用的对象、本地方法栈内引用的对象、静态属性引用的对象、常量引用的对象、同步锁 synchronized 持有的对象、虚拟机内部的引用、反映 java 虚拟机内部情况的 JMXBean、JVMTI 中注册的回调、本地代码缓存等
强引用:new出来的对象就是强引用,垃圾回收器不会回收
软引用:new SoftReference(),内存不足时回收
弱引用:第一次GC回收时,扫到了回收。ThreadLocal的map的key是弱引用
虚引用:虚引用主要用来跟踪对象的回收,清理被销毁对象的相关资源。

按功能分:输入流、输出流
按类型分:字节流(8个字节为单位)、字符流(16)
BIO:同步阻塞IO,使用简单,并发处理能力低
NIO:同步非阻塞IO,通过channel实现多路复用
AIO:异步非阻塞IO,基于事件和回调机制
将文件或文件的一部分映射到内存中的技术。通过内存映射文件,可以以字节数组的方式访问文件内容,而无需进行昂贵的系统调用。
把当前jvm进程中的对象,传输到另一个进程里面进行恢复。序列化后进行传输,恢复就是反序列化。
常用的序列化工具json、xml、kyro、hessian等
1、客户端请求建立连接
2、服务端应答,并请求建立连接
3、客户端确认客户端应答
1、客户端请求断开连接
2、服务端确认应答
3、服务端请求断开连接
4、客户端确认应答
503 错误是 HTTP 协议中的一种服务器错误,表示服务器暂时无法处理请求。这个错误通常发生在服务器维护、过载或者遇到了意外的问题时。
1、首先从main找到run方法,在执行run之前new一个springApplication对象
2、进入run方法,创建应用监听器
3、加载配置文件
4、加载应用上下文,当作run方法的返回对象
5、创建spring容器,实现自动配置和bean的实例化
两个阶段:容器启动阶段、bean实例化阶段
容器启动阶段:加载元数据、对各种处理器进行注册、上下文初始化、事件广播初始化
Bean实例化:
1、加载Bean定义:spring从配置文件或注解中加载bean定义
2、实例化Bean: 根据bean的定义,创建bean实例。如果bean定义中指定了初始化方法,spring会在实例化后调用这些方法。
3、属性赋值:如果bean中定义了属性,spring会使用属性值或构造器参数对属性进行赋值。
4、Bean注册:将Bean实例注册到Spring容器中,以便在其他地方使用。
ioc中文名控制反转(另一名称DI依赖注入)。IOC是指,利用反射的原理将创建对象的权利交给Spring容器,spring在运行的时候根据配置文件来动态的创建对象和维护对象之间的关系,实现了松耦合的思想。
面向切行期间,不修改源码对已有方法进行增强。
配置方式:基于注解配置aop、基于XML的Aop、编程式创建代理
Springboot中的类SPI扩展机制
在springboot的自动装配过程中,最终会加载META-INF/spring.factories文件,而加载的过程是由SpringFactoriesLoader加载的。从CLASSPATH下的每个Jar包中搜寻所有META-INF/spring.factories配置文件,然后将解析properties文件,找到指定名称的配置后返回。需要注意的是,其实这里不仅仅是会去ClassPath路径下查找,会扫描所有路径下的Jar包,只不过这个文件只会在Classpath下的jar包中。
jvm的类加载器默认使用的是双亲委派模式。三种默认的类加载器 启动类加载器、扩展类加载器、应用程序类加载器。每一个中类加载器都确定了从哪一些位置加载文件。也可以通过继承java.lang.classloader实现自己的类加载器。
当一个类加载器收到类加载任务时,会先交给自己的父加载器去完成,因此最终加载任务都会传递到最顶层的启动类加载器,只有当父加载器无法完成加载任务时,才会尝试自己来加载。
A依赖于B,B依赖于C,C又依赖于A
解决办法:
1、Spring首先从一级缓存“SingletonObjects”中获取。
2、获取不到并且对象正在创建中,就再从二级缓存“earlySingletonObjects”中获取。
3、如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取。
4、如果从三级缓存中获取到就从singletonFactories中移除,并放入earlySingletonObjects中。其实也就是从三级缓存移动到了二级缓存。
无法解决的循环依赖:A依赖于B,B依赖于A
Spring里面的事务本质上是数据库层面的事务,主要是针对单个库中的对个表的操作。
分布式事务主要解决多个库直接数据一致性问题。
1、使用数据库事务: MySQL 的 XA 事务、 Oracle 的 RAC 事务
2、分布式事务中间件:例如,可以使用 Spring 框架提供的分布式事务支持,通过配置事务管理器、事务传播行为等来实现分布式事务。
3、使用消息队列:将事务操作转化为消息,通过消息队列进行异步处理,保证事务的最终一致性。
4、采用分布式锁:通过对分布式环境中的共享资源进行加锁,保证事务的原子性和一致性。可以使用如 Redis、ZooKeeper 等分布式锁实现。
5、采用分布式缓存:通过将数据缓存在分布式缓存中,减少对数据库的访问,提高并发性能,同时保证数据的一致性。如使用 Redis、Memcached 等分布式缓存技术。
6、使用 TCC机制:分为三个阶段,分别在事务主动方、事务被动方和协调器之间进行。通过 try、commit 和 confirm 操作来保证分布式事务的一致性。
1、什么是分布式锁?
系统内,有多个消费者,需要对同一共享数据并发访问和消费时,会有线程安全问题。例如在秒杀、抢优惠券等场景下,商品库存的数量是有限的,在高并发下,会有"超买"或"超卖"的问题。因此我们需要使用锁,解决多线程对共享数据并发访问的线程安全问题。
2、为什么要用分布式锁
当系统使用分布式架构时,服务会有多个实例存在。需要使用分布式锁,保证一个资源在同一时间内只能被一个服务的同一个线程执行。
3、redis分布式锁
通过代码调用setnx命令,只在键不存在的情况下, 将键的值设置为某个值。若键已经存在, 则setnx命令不做任何动作。为了能处理"获取该锁的请求所在的服务实例宕机,会导致该资源被长期锁住,其他请求无法获取该锁"这种情况,我们还需要设置超时时间。
4、redis分布式锁缺陷
1、强依赖redis的可用性,一旦redis宕机,会导致业务系统不可用,因此最好搭建redis集群。
2、因为对锁设置了超时时间,如果某次请求不能在该次限制时间内完成操作,也会导致在某些时刻,多 个请求获取到锁。
解决方案也很简单,我们在调用setnx时,将值设置为该次请求线程的id,并且在服务实例内,设置一个守护线程,当锁快要超时时,判断请求是否完成,如果未完成,延长超时时间。
原子性、
一致性(事务执行之前数据库是一致的,那么执行后也是)、
隔离性(事务间隔离)、
持久性(数据的改变是持久的)
读未提交:能读取到其他事务没提交的,脏读、不可重复读、幻读
读已提交:读取到其他事务提交后的,不可重复读、幻读
可重复读:给事务读取的数据加上版本号,一个事务内两次读取一致,幻读
串行化:一个事务完成才能执行下一个事务
脏读:读到其他事务还没有提交的,可能后面又发生了回滚或修改,就是脏数据
不可重复读:同一条数据,同一个事务中两次读取结果不一样
幻读:同一个事务两次读取的数据条数不一样

1、B+树通过对数据进行排序可以提高查询效率
2、一个节点储存多个元素,(B树节点索引+数据,B+树节点仅索引,叶子节点索引+数据)使得B+树不会太 高(一个innodb页默认16k,一般两层B+树存2千万左右数据)
3、叶子节点存了所有数据,可以很好的支持全表扫描、范围查询
4、由于B+树不会太高,一般三层,B+树的IO次数比较稳定
行锁:锁住一行数据,并发度高
表锁:锁住整个表,并发读低(MyISAM 存储引擎只支持表锁)
间隙锁:锁住某个区间
1、binlog用于记录数据库执行的写入性操作,只有在事务提交时才会记录biglog,binlog日志有三种格式,分别为STATMENT、ROW和MIXED。(使用场景:主从复制、数据恢复)。
2、Redolog(InnoDB特有的)记录数据页的变更,而这种变更记录是没必要全部保存。mysql每执行一条DML语句,先将记录写入redo log buffer,后续再一次性将记录写到redo log file。(因为Innodb是以页为单位进行磁盘交互的,1、一个事务很可能只修改一个数据页里面的几个字节,这个时候将完整的数据页刷到磁盘,太浪费资源。2、一个事务可能涉及修改多个数据页,并且这些数据页在物理上并不连续,使用随机IO写入性能太差)
注意:binlog和redo log二者同时记录,才能保证当数据库发生宕机重启时,数据不会丢失。
redolog 事务日志、binlog数据库变更的逻辑日志
3、undolog保证事务原子性,记录了数据的逻辑变化。比如一条INSERT语句,对应一条DELETE的undo log,对于每个UPDATE语句,对应一条相反的UPDATE的undo log,这样在发生错误时,就能回滚到事务之前的数据状态。
Mysql开启了binlog日志时,那么提交事务时就要同时完成redolog和binlog的事务写入。二段提交发生在redolog和binlog的日志写入阶段,为了满足数据的一致性设计。
第一阶段:prepare阶段,事务操作记录到redolog中 ,记录为prepare状态。
第二阶段:commit阶段,事务操作记录到binlog中,把redolog的状态改为commit。
如果在写入redolog之前崩溃,redolog和binlog中为空,满足一致性
如果在写入redolog后,写入binlog前崩溃,redolog中为prepare状态状态,可根据redolog恢复。
如果的在binlog写入后崩溃,那么redolog可以拿着事务id去执行binlog的日志直接提交数据。
1、没有使用索引列作为where查询条件
2、对索引列进行函数操作(字符串操作、日志操作)
3、对索引列进行类型转换
4、Like查询时以“%”开头
5、查询条件包括Or,or条件中的每个条件都不涉及索引列,mysql无法使用索引
6、当使用大范围查询的时候
缓存雪崩:大量key同时失效
击穿:多个请求获取一个key,key失效时发生击穿到数据库
穿透:缓存中没有key,数据库中也没有,造成资源一直访问数据库
1、系统不主动轮询,等用户登陆系统时触发检查(缺点:无法主动发送提醒)
2、用搜索引擎(如es),存储一份会员ID和过期时间到搜索引擎中,搜索引擎能够实现快速检索
3、使用redis实现,把id和过期时间存到redis中,使用redis过期提醒功能 ,当key过期后触发过期事件。
4、使用MQ里面的延迟队列,用户开通会员后,计算会员过期时间,发送延迟消息到MQ里面,达到时间进行消费。
注意:
1、调用完notifyAll()方法后,同步代码块中的其他代码,必须执行完后才能将对象锁释放,而不是调用了notifyAll()方法后立即释放。
2、在java中,Thread类线程执行完run()方法后,一定会自动执行notifyAll()方法。
ThreadLocal是一种线程隔离机制,提供了多线程环境下对于共享变量访问的一个安全性。
在每个线程里面都有一个容器,来存储共享变量的一个副本,然后每个线程只对自己的变量副本进行更新操作。
实现原理:
在Thread类里面有一个ThreadLocalMap,用来存储共享变量的副本,线程仅对这个副本进行操作,不影响全局共享变量的值,实现数据隔离。
1、降低线程开启关闭消耗的系统资源
2、提高系统响应速度(线程开启响应速度慢)
3、线程管理(线程开启、关闭、线程数量等)
简述:任务进线程池,先判断核心线程是否满了,满了就进队列,队列满了就看最大线程数是否已达到,达到了就按饱和策略处理。未达到饱和就创建线程执行任务。

核心线程 - 队列 - 最大线程数 - 饱和策略
1、固定大小线程池
2、缓存线程池
3、单线程线程池
4、延迟任务线程池
5、单线程延迟任务线程池
6、抢占式线程池
7、自定义线程池(ThreadPoolExecutor 7个参数)
1、核心线程数
2、最大线程数
3、线程存活时间
4、线程存活时间单位
5、工作队列
6、线程工厂
7、拒绝策略
1、直接提交队列(没有容量,插入一个就会阻塞,执行删除才会唤醒)
2、有界任务队列(达到队列最大值,开启新的线程,否则一直是核心线程,该队列现进先出,有数组和链表两种实现)
3、无界任务队列(这种队列下最大线程数是无效的,只有核心线程)
4、优先任务队列(特殊的无界队列,使用平衡二叉树堆实现,排队时带有优先级)
1、丢弃任务抛弃异常
2、直接丢弃任务
3、丢弃队列最前面的任务
4、调用线程(提交任务的线程)直接执行此任务
5、new RejectedExecutionHandler() 自定义拒绝策略
shutdown 当任务队列为空的时候,才关闭线程池
shutdownnow 直接停止
会的,newFixedThreadPool使用了无界的阻塞队列LinkedBlockingQueue,如果线程获取一个任务后,任务的执行时间比较长,会导致队列的任务越积越多,导致机器内存使用不停飙升, 最终导致OOM。
1、继承Thread,重写run方法
2、实现Runnable接口创建线程(无返回值)
3、实现Callable接口+Future创建线程(有返回值,可抛出经过检查的异常)
可以对正在执行的任务进行维护操作(取消任务、获取任务执行结果、判断任务是否已完成等)。
1、可取消的异步计算;
2、利用开始和取消计算的方法、查询计算是否完成的方法和获取计算结果的方法,此类提供了对Future的基本实现;
3、可使用FutureTask包装Callable或Runnable对象因为FutureTask实现了Runnable,所以可将FutureTask提交给Executor执行;
4、仅在计算完成时才能获取结果;如果计算尚未完成,则阻塞get方法一旦计算完成,就不能再重新开始或取消计算;
i++不是原子性操作,执行它包括三个操作:读i的值、i+1、将新值保存到内存。
所以它不是线程安全的。
可重入:当一个线程获得一个对象锁后,再次请求该对象锁时,可以再次获得该对象的锁。
1、都是独占锁
2、都是可重入的
1、ReentrantLock是Java层面的实现,手动加锁手动解锁,synchronized是JVM层面的实现。
2、ReentrantLock可以实现公平和非公平锁。
3、ReentantLock获取锁时,限时等待,配合重试机制更好的解决死锁
4、ReentrantLock可响应中断
5.使用synchronized结合Object上的wait和notify方法可以实现线程间的等待通知机制。ReentrantLock结合Condition接口同样可以实现这个功能。
1、每一个可重入锁都会关联一个线程ID和一个锁状态status。
2、当一个线程请求方法时,会去检查锁状态,
3、释放锁时,
Activity(活动)、workflow(流程)、workItem(工作项)、rule(规则)、状态(state)
1、提供了可视化的方式进行描述和执行业务流程
2、支持业务逻辑的可重用性和模块化开发
3、支持监控和追踪业务流程
4、扩展性比较好
1、RepositoryService 流程定义和部署对象
2、RuntimeService 执行管理,包括流程实例和执行对象
3、TaskService 执行任务(正在流程中的)
4、HistoryService 历史管理
5、IdentityService 用户角色
流程实例:一个流程中,流程实例只有一个
流程对象:按照流程定义的规则执行一次操作,一个流程中执行对象可以多个
1、使用流程变量
2、当一个任务完成之后,根据这几条连线的条件和设置流程变量,例如${流程变量名==’值’},{}符号是布尔类型,判断走哪条线。
排他网关:分支,通过连线的流程变量,判断执行哪条连线,如果条件不符合,默认离开连线。只执行其中一个流程
并行网关:可以同时执行多个流程,直到总流程结束。可以对流程进行分支和聚合。
1、直接给值,在Xxxx.bpmn中指定
2、流程变量${流程变量名}或#{}
3、用监听类指定任务办理人(setAssgnee)
1、不一样
2、都是用TaskService完成
3、个人任务(taskAssgnee),组任务(taskCandidateUser)
4、数据库存放,个人任务:参与,组任务:类型、参与、候选
一般情况可是循环引用、内存对象泄露没有销毁、动态分配内存后没有释放、长期持有对象引用、资源未关闭等情况造成的内存泄露