Java
集合
扩容
- ArrayList 长度*1.5
- Vector 长度*2
- HashMap 长度*2
算法
什么是二叉树
每个节点上有两个子节点的叫二叉树
什么是红黑树
红黑树是一种特殊的二叉树,每个节点是红色或者黑色
其中根节点必须是黑色,红色节点的子节点一定是黑色
每个节点到叶子节点的所经历的黑色节点的个数一样
并发
并发的三个必要因素
- 原子性:操作不能被分割,要么全部执行成功,要不全部执行失败
- 可见性:一个线程修改共享变量之后,其他线程要立刻能够看到
- 有序性: 必须按照代码中的顺序执行(处理器可能会对指令进行重排)
如何保证程序能够安全运行
解决也是从三要素解决
- 线程切换带来的原子性问题, 使用synchronize,lock
- 缓存导致的可见性问题,使用synchronize,lock,volatile
- 代码编译期间带来的有序性问题,Happens-Before规则
并发和并行的例子
- 并发:两个人同时使用一台电脑
- 并行:两个人使用两台电脑
- 串行:两个人排队使用两台电脑
什么是进程,线程
- 进程:正在运行中的程序就是一个进程
- 线程:进程中的一个独立的控制单元 两者在用户感受上的区别:电脑中的正在开启的微信就是一个进程,微信中的一个接收新消息的单元叫线程
两者在本质上的区别:进程是操作系统资源分配的基本单位,线程是处理器进行任务调度的基本单位,不同进程之间的地址空间和资源是独立的,同一进程之间的线程地址空间和资源是共享的
什么上下文切换
-
前因:由于一个cpu核心每时每刻只能处理一个线程,而线程数往往大于cup核心数量.所以为了让这些线程都能被调度处理,cpu采取了时间片轮转策略.就是为先为每个线程分配一段时间片,在当前时间片内,cpu就会处理这个线程,到达时间之后,当前线程就会重新变为就绪状态并且将cpu让给其他进程
-
后果:当cpu在执行一个时间片之后把线程的状态保存,下次重新执行的时候再加载出来.这过程就是上下文切换.上下文切换是非常频繁的,甚至每秒可以切换上百次
什么是守护线程和用户线程
- 用户线程:运行在前台的线程 如何main函数
- 守护线程: 运营在后台的线程,目的就是为前台线程服务,如垃圾回收
当程序中的用户线程全部执行结束之后,守护线程也会跟随结束。 守护线程的角色就像“服务员”,而用户线程的角色就像“顾客”,当“顾客”全部走了之后(全部执行结束),那“服务员”(守护线程)也就没有了存在的意义,所以当一个程序中的全部用户线程都结束执行之后,那么无论守护线程是否还在工作都会随着用户线程一块结束,整个程序也会随之结束运行。
什么是线程死锁
死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(线程)称为死锁进程(线程)
比如两个人需要用一台电脑,一个人拿着显示器,另个人拿着主机,两个人都在等对方用完,但是永远都没办法使用
如何避免形成死锁
- 避免一个线程同时获得多个锁
- 避免一个线程在锁内同时占有多资源
- 使用定时锁待代替内部锁机制
创建线程的方式
- 继承thread类,重写run方法
- 实现runnable接口,重写run方法
- 实现callable接口,重写call方法(有返回值的线程类,调用FutureTask.get()获取值)
start()方法和run()方法区别
run()方法内部放着,当前线程需要执行的内容 start()方法是将当前线程有new时的新建状态改为就绪状态,等待时间片分配之后执行 直接调用run()方法相当于调用一个普通方法,在主线程中执行
线程的不同状态
- 新建(new) 创建一个新线程
- 就绪(runnable) 调用线程的start()方法,该线程就处于就绪状态,等待cpu调度
- 运行(running) 就绪状态的线程一旦获取时间片,就变成了运行状态,注意线程只能通过就绪状态,转为运行状态
- 阻塞(block) 运行中的线程由于某些原因,暂时放弃对cpu的使用权,进入阻塞状态,只有其进入到就绪状态,才有可能再次变成运行状态 阻塞转台分为等待阻塞和其他阻塞两种情况
- 死亡(dead) 线程的run()方法执行完毕后,或者因为异常退出run()方法,该线程就进入死亡状态,死亡状态无法转为其他状态
线程同步以及线程调度的方法
- wait() 让线程变为等待阻塞状态(会释放持有的锁)
- sleep() 让线程变为休眠状态(不会释放持有的锁)
- notify() 唤醒处于等待阻塞状态下的线程
- notifyAll() 唤醒所有处于等待阻塞状态下的线程
Java中的调度线程算法
- 分时调度模型: 所有线程轮流获取cpu使用权,平均分配每个线程使用cpu的时间
- 抢占调度模型: Java虚拟机采用的这个模型,根据线程的优先级来决定线程的运行顺序
正确使用wait()方法的方式
先使用synchronize锁线程对象,再使用while循环判断条件
synchronized (monitor) {
// 判断条件谓词是否得到满足
while(!locked) {
// 等待唤醒
monitor.wait();
}
// 处理其他的业务逻辑
}
实现线程同步的方法
- 同步方法 使用synchronize修饰方法
- 同步代码块 使用synchronize修饰代码块
- 同步变量 使用synchronize,volatile修饰变量
- 使用手动锁lock
Lock lock = new ReentrantLock();
lock. lock();
try {
System. out. println("获得锁");
} catch (Exception e) {
// TODO: handle exception
} finally {
System. out. println("释放锁");
lock. unlock();
}
什么是线程的优先级
高优先级的线程会有优先执行的权利,但是并不能保证一定按照顺序执行,优先级中1-10,1代表最低,10代表最高
java中线程通信过程
什么是java监视器(monitor)
在jvm中,监视器是和锁一起使用的,监视器会监视一段代码,保证一次只能有一个线程执行这段代码,线程在获取锁之前是不是执行这段代码的
关键字synchronize
保证在多线程下,被synchronize修饰的方法,代码块不会被同时执行,底层实现通过monitor
- 修饰代码块,为当前对象枷锁
- 修饰实例方法,为当前对象加锁
- 修饰静态方法,为class类加锁
单例模式
懒汉模式
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getUniqueInstance() {
//先判断对象是否已经实例过,没有实例化过才new
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
饿汉模式
public class Singleton {
//直接new,由于static是在类被加载的时候执行的,所以只会有一份
private static Singleton uniqueInstance=new Singleton();
private Singleton() {}
public static Singleton getUniqueInstance() {
return uniqueInstance;
}
}
由于懒汉模式先判断是否为null,再去new对象,所以可能存在并发问题 饿汉模式由于是在类被加载的时候new的对象,所以不会有并发问题,但是不确定是否会使用就new对象,会造成不必要的浪费
懒汉模式(双重锁class)
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getUniqueInstance() {
//先判断对象是否已经实例过,没有实例化过才new
if (uniqueInstance == null) {
synchronized(Singleton.class){
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
先使用volatile 防止指令重排,再添加synchronize锁当前class
synchronize锁升级的原理是什么
在锁对象头中有一个threadId字段,第一次访问的时候为null,jvm让其持有偏向锁,然后设置threadId为当前线程id,再次进入的时候会去判断threadId是否为当前线程id,如果是正常执行,如果不是将锁升级为轻量锁,通过自旋循环一定次数来获取锁,如果到达一定次数之后,还没有获取要使用的对象,就会把锁升级为重量级锁
- 偏向锁:会偏向第一个访问的线程,如果被别的线程抢占锁之后,持有偏向锁的线程会被挂起,然后将锁升级到轻量级锁
- 轻量级锁:由偏向锁升级而来
- 重量级锁:由轻量级锁升级而来
synchronize,volatile和CAS比较
- synchronize是悲观锁,会抢占资源,导致其他线程阻塞
- volatile 提供共享变量可见性和禁止指令重排
- CAS 是基于冲突检测的悲观锁
synchronize和Lock区别
- synchronize是关键字, Lock是一个类
- synchronize能给类,代码块,方法加锁,而Lock只能给代码块加锁
- synchronize不需要手动释放锁,出现异常之后会自动释放锁,不会造成死锁,而Lock加锁使用之后需要手动unLock()去释放锁
乐观锁和悲观锁的区别
- 悲观锁:总是假设最坏的情况,认为每次拿到的内容可能被别人修改,所以拿到内容之后就给上锁,这样别人就无法获取更改数据.悲观锁在数据库中很常见,比如 行锁,表锁,读锁,写锁等,在java中synchronize也是悲观锁
- 乐观锁:总是假设最好的情况,认为每次拿到的内容可能没有被别人修改,只是在更新的时候使用版本号机制判断数据有没有更新过
线程池
什么是线程池
java线程池 是运用场景最多的框架,几乎所有的并发任务都可以使用线程池,合理使用线程池能带来许多好处,但是如果一个线程执行时间非常久的话就不适合使用线程池(没有频繁创建和销毁,不能控制线程开始,挂起和终止)
- 降低资源消耗,不用频繁创建线程和销毁线程
- 提高响应速度,防止突然爆发大量线程导致效率问题
- 线程管理管理可见性,统一分配,调优和监控
ThreadPoolExecutor的用法
我们一般通过Executors工厂类 通过构造不同的参数,来创建不同场景下的线程池,参数介绍
- corePoolSize 核心线程数
- maximumPoolSize 最大线程数量
- keepAliveTime 线程存活时间 以unit为单位
- unit 线程存活时间单位
- workQueue 阻塞队列
- threadFactory 线程工厂
- handler 线程拒绝策略
线程池的四种创建方式
- newCacheedThreadPool 可缓存线程,默认线程长度为integer最大值,可以修改,可以认为是无限长度
- newFixedThreadPool 指定长度的线程池
- newScheduledThreadPool 创建一个可以定时周期性执行任务的线程池
- newSingleThreadExecutor 创建一个单线程化的线程池,只会用唯一的工作线程来执行任务,保证任务按照指定顺序执行
线程池的submit()方法和execute()方法区别
两者相同点就是都可以开启线程池中的任务 不同点就是execute()只能执行Runnable类型无返回值的任务 而execute()可以执行runnable类型和Callable类型有返回值的任务
如何确定线程池构建的参数
线程等待的时间比CPU执行的时间高,需要更多线程 线程等待的时间比CPU执行的时间地,需要更少线程
垃圾回收
如果对象的引用设置为null,垃圾回收器是否会立即释放内存
不会,设置为null之后,在下一次进行垃圾回收的时候才会释放内存
finalize()方法什么时候调用
当垃圾回收器决定回收对象的时候,就会调用该对象的finalize()方法 然后在下次垃圾回收的时候,才会释放该对象的内存
异常
运行时异常和受检查异常
RuntimeException异常和受检异常之间的区别:是否强制要求调用者必须处理此异常,如果强制要求调用者必须进行处理,那么就使用受检异常,否则就选择非受检异常(RuntimeException)。一般来讲,如果没有特殊的要求,我们建议使用RuntimeException异常。
try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
会执行,在catch中的return()方法执行之前,先执行finally中的代码
内存模型
jvm的内存模型
- 方法区:存储已经被虚拟机加载的类,常量,静态变量
- 堆内存: 所有线程共享,所有对象的实例都在这里分类内存
- 虚拟机栈:每个方法在执行的过程中都会在虚拟机栈中创建一个栈帧,存储局部变量,操作数栈
- 本地方法栈:存放native的c和c++代码
- 程序计数器:当前线程所执行的字节码行号
描述方法区的作用
所有线程共享,用来存储 常量,静态变量,类信息等
描述程序计数器的作用
存放当前线程正在执行的字节码指令地址
1.当线程A在看直播
2.线程B突然来了一个视频电话,就会抢夺线程A的时间片,打断线程A,此时线程A被挂起
3.线程B结束之后,线程A不知道要干什么(因为线程不具备记忆功能,只负责执行动作,所以记忆功能就是由程序计数器来实现)
描述虚拟机栈的作用
虚拟机栈是线程私有的,生命周期和线程相同
每个方法执行的时候都会创建一个栈帧,用来存放局部变量,操作数栈,动态链接等
描述java堆内存
- java heap是jvm所管理的最大区域,被所有线程共享,在虚拟机启动的时候创建,堆内存的唯一目的就是存放对象实例
- java堆是垃圾收集器的主要管理区域,因此也被称为GC堆
- 从内存回收的角度可以分为新生代和老年代
- java堆在物理上是不连续的存储空间,当前主流的虚拟机都是可以扩展的通过启动参数-Xmx和-Xms
堆和栈的区别
- 物理地址上,堆内存物理地址上可以不连续,因此性能较慢,栈内存使用的数据结构中的栈,物理地址是连续的,所以性能更快
- 内存大小上,堆内存物理地址不连续,所以在运行期内存大小不确定,栈内存是连续的,所以大小已经固定
- 存放的内容,堆内存上放的是对象的实例和数组,因此堆内存更关心数据的存储.栈内存上放的是局部变量,操作数栈,返回结果,所以栈内存更关心方法的执行
浅拷贝和深拷贝的区别
- 浅拷贝是指只增加了一个指针指向当前地址,
- 深拷贝是指,复制了一个新的地址,并且指针指向新地址
简单描述一下垃圾回收机制
在java中,不需要程序员手动释放对象的内存,而是由虚拟机中的垃圾回收线程执行.在虚拟机空闲的时候或者当前堆内存不足时才会触发回收,将没有被引用的对象进行垃圾回收
- 优点 jvm自己进行垃圾回收,不再需要开发人员手动处理
- 缺点 程序员不能实时对某个对象进行垃圾回收
java中的引用类型
- 强引用: 发生gc时也不会回收
- 软引用: 在内存即将溢出时会被回收
- 弱引用: 在gc时会被回收
- 虚引用: 可能自己就会消失
如何判断对象可以被回收
- 引用计数法:对象创建的时候指定一个技术,被引用的时候+1,被释放的时候-1,当计数器为0的时候就可以被回收,缺点就是不能处理循环引用
- 可达性分析:从GC ROOTS 开始向下搜索,当一个对象的GC ROOTS找不到任何引用链时,证明对象可以被回收
什么是full gc
清理整个堆空间的年轻代,老年代,永久代内存. 因为full gc是清理整个堆空间,所以执行速度很慢,开发中应尽量避免full gc 行为
jvm垃圾收回算法有哪些
- 标记-清除: 标记无用对象,然后进行清除
- 复制: 标记无用对象,将有用对象复制到另一端,然后清除这一端的所有对象
- 标记-整理: 标记无用对象,将有用对象移动到另一端,然后清除这一端所有对象
- 分代: 根据对象存活时期不同,将内存分为几代
垃圾回收会发生在永久代吗
不会,如果永久代满了之后,只会触发full gc
新生代,老年代,永久代区别
在java中堆内存被划分为三个区域,年轻代,老年代,永久代,而年轻代又被划分为三个区域
伊甸园区,存活1区,存活2区
- 新生代:存在的都是新的对象,所以每次垃圾回收都会处理大量对象,只有少量存活,所以采用复制算法,只复制少量对象
- 老年代:存放的都是活了很久的对象,不太经常会进行垃圾回收,所以采用标记-清理算法,或者标记-整理算法
- 永久代: 存放类加载信息,静态变量,常量等数据,这些东西比老年代更不容易发生gc
Minor GC,Major GC,Full GC是指什么
- Minor GC是发生在新生代的GC,由于大部分对象都是朝生夕死的,刚刚被创建出来,可能就被销毁了.当eden区满了的时候就会触发一次,所以Minor GC出现的非常频繁,采用的是复制算法
- Major GC是发生在老年代的GC,由于不太经常执行,所以速度比起Minor GC慢了很多,采用的是标记-清除算法和标记-整理算法
- Full GC是指整个堆内存的清理,包括新生代和老年代
为什么新生代要区分一个Eden区和两个存活区
如果新生代只有一个区域的话,当Eden区满了就会触发Minor GC,如果直接把对象送到老年代,老年代的内存空间远远大于新生代,当老年代满了之后进行的Major GC的时间也会远远大于新生代的Minor GC
存活区的意义就是减少被送到老年代的数量,只有经历15次Minor GC的对象才会进入老年代,两个存活区的的意义是为了减少内存碎片化的发生
老年代,新生代,新生代三个区域的大小默认比例
老年代:新生代 2:1 Eden区:存活区一:存活区二 8:1:1
常用的jvm调优命令
-Xms:初始堆大小
-Xmx:最大堆大小,jvm运行过程中如果初始堆内存不够,最大可以扩展到多少
-Xmn:堆内存中年轻代大小
-Xss:设置每个线程的栈内存大小
-Xnoclassgc:是否禁用垃圾回收机制
redis
redis基本特征
redis的优缺点
优点
- 读写性能优异
- 支持持久化,AOF和RDB两种方式
- 支持事务,redis的所有操作都是原子性的,还可以将多个指令合并为一个原子性操作
- 数据结构丰富 string,hash,set,zset,list
- 支持主从复制,主机会自动同步数据到从机,意味着可以读写分离 缺点
- 数据库容量受到物理内存限制,只适合较小数据量
- redis宕机之后,如果有数据未同步到从机,主机恢复之后也不会再次同步到从机
- redis不能在线扩容,使用之前必须指定大小
为什么要使用redis
- 高性能,速度非常快,由于数据存放在内存中所以响应速度小于磁盘中的数据库
- 高并发,单线程结构,不存在线程之间切换,所以不会加锁释放锁的问题
- 可以设置到期时间,非常适合缓存数据
redis为什么这么快
- 完全基于内存,类似hashMap结构,优势就是查找和操作的时间复杂度都是O(1)
- 采用单线程,避免了线程之间的切换,不存在加锁释放锁的操作,不可能存在死锁问题
- 使用多路复用非阻塞IO模型
redis有哪些数据结构
- string: 简单的键值对操作
- list: 列表型数据,粉丝列表,评论列表
- set: 去重的列表型数据,常用来做集合之间的交集,并集,差集操作
- zset: 有指定顺序的无序列表,用来做排行榜
- hash: 结构化的数据,类似的对象
使用redis的场景
- 缓存热点数据,页面广告
- 缓存登录凭证,将用户身份信息token放在redis中,多台机器可以共享
- 消息队列,可以通过list进行Lpush和Rpop的操作来当做消息队列使用
- 分布式锁 setnx命令可以实现分布式锁
redis解决问题的原理和算法
redis持久化
- RDB: 将内存的快照保存到磁盘中
- AOF: 将每个写的指令保存到磁盘中 AOF的形式生成的文件非常大,但是相对稳定, RBD的形式文件比较小,恢复起来效率更快,但是会丢失数据
redis的过期淘汰策略
- 定时器: 每个key创建的时候生成一个定时器,到达时间就会执行删除操作.这样会耗用大量cpu来处理过期数据
- 惰性删除: 值到时不会自动删除,只会在下次查询的时候,先去判断是否过期,如果过期才会去删除.这样会造成大量无用数据,没有返回就无法删除
- 轮询删除:执行一定的频率,扫描过期数据,找到的话就删除 redis使用的就是惰性删除和轮询删除两种搭配使用
redis内存不足时淘汰策略
- 全局健 随机删除/移除最少使用/报错
- 过期健 随机删除/移除最少使用/移除最早到期
redis集群方案
- 哨兵模式,至少三个以上的哨兵实例,来监控redis的主从服务是否可用,哨兵不能保证数据不会丢失,只能保证redis的高可用性
- 官方redis cluster(服务端路由查询)
- 基于客户端分配
redis穿透,雪崩,击穿
- 穿透: 如果为null不缓存数据,导致每次都去请求数据库(设置缓存null数据)
- 雪崩: 值设置相同到期时间,导致大量数据同时到期,很多请求命中数据库,导致数据库宕机,形成一系列连锁反应(设置不同的到期时间)
- 击穿: 高并发情况下,一个缓存key到期,多个请求同时过来,就会都访问数据库(分级缓存,定定时自动更新)
缓存预热
上线之后,现在不开放外网,在内网环境下,将数据添加到缓存中,再去开启外网
缓存降级
当redis出现问题之后,不去查询数据库,而是直接返回默认值给用户
mybatis
mybatis基本特征
mybatis优点
mybatis是一款半自动orm框架,封装了JDBC,让开发人员不再关注注册驱动,建立连接,释放连接等过程,只关注SQL语句本身就可以,将SQL语句和java拆分开来,放在xml文件中
mybatis和hibernate的区别
- mybatis半自动框架,需要手动编写SQL语句,但是可以动态拼接SQL参数,只用处理SQL语句和数据库表的关联就可以.但是限制了数据库是的种类
- hibernate全自动框架,映射java对象和数据库表,不用再去手动拼写SQL语句,提供了自创的HQL语句,
mybatis使用注意内容
mybatis的#{}和${}区别
- #{}预编译形式,防止SQL注入
- ${}占位符形式,可以传递任何变量
模糊查询like应该如何使用
绑定的时候使用"'%'+value+'%'",SQL语句中使用#{}
<select id="listUserLikeUsername" resultType="com.jourwon.pojo.User">
<bind name="pattern" value="'%' + username + '%'" />
select id,sex,age,username,password from personwhere username LIKE #{pattern}
</select>
多个查询参数如何传递
- 多个值得情况下 接口中使用@Param声明参数名称;xml文件中使用#{参数名称}
- 单个对象的情况下 xml中使用parameterType声明全类名,在使用#{字段名称}
mybatis中如何批量操作
使用foreach标签拼接,foreach标签属性
- item 循环对象
- index 循环索引
- open 该语句以什么开始
- separator 每次循环中的间隔符
- close 该语句以什么结束
mybatis如何获取新增的id值
- xml文件中在insert标签中使用 keyProperty="id"
- 接口中使用id作为方法返回值
当实体类和数据库字段名称不同时
- 查询的时候使用别名
- 使用resultMap标签配置property和column转换
事务
事务的(ACID)概述
- 原子性
- 一致性
- 隔离型
- 持久性
MySQL
MySQL基本内容
数据库的三范式
- 第一范式 每一属性都可以再做分割,不允许表中有表
- 第二范式 其他属性必须依赖主属性
- 第三范式 其他属性之间不能存在依赖传递现象
MySQL中的binlog有哪几种录入格式
- statement 每修改一条数据的SQL都是被记录在binlog中,保存执行的时候上下文对象
- row 不记录SQL上下文的信息,记录每一行的改动.但操作过多,这种模式生成的文件非常大
- mixed 普通操作使用statement记录,无法使用statement时使用row记录
MySQL使用注意点
索引的优缺点
- 优点: 加快查询速度
- 缺点: 创建索引和维护索引需要浪费时间,减低增删改查的效率,索引占用物理空间
索引的分类
- 主键索引: 主键字段
- 唯一索引: 值只能存在一个(忽略对null的判断)
- 单列索引: 单个列的普通索引
- 复合索引: 多个属性组合为一个索引
索引的使用场景
- 新增/修改的不频繁,查询频繁
- 多个字段经常查询
- 表中行的基数非常大
索引的数据结构
- B树索引
- Hash索引
创建索引时应该注意什么
- 尽量指定非NULL字段
- 尽量选用长度较小的字段,或者指定索引的长度
什么是数据库事务
- A 原子性 事务的中的操作是最小单元,事务中的操作要么全部执行,要么全部回滚
- C 一致性 执行事务前后,多个事务对同一个条数据的读取结果相同
- I 隔离性 不同事务之间隔离
- D 持久性 事务一旦执行,对数据库的影响是持久的,不会因为异常而改变
事务的隔离级别
MySQL定义了四种隔离级别从低到高 READ-UNCOMMITTED,READ-COMMITTED,REPEATABLE-READ,SERIALIZABLE
- READ-UNCOMMITTED 读取未提交,允许读取未提交的数据,可能会导致脏读,幻读,不可重复读
- READ-COMMITTED 读取提交,允许读取并发事务已经提交的数据,可能会导致幻读和不可重复读
- REPEATABLE-READ 可重复读,对同一字段多次读取结果相同,可能导致幻读
- SERIALIZABL 可串行化, 最高的隔离级别,所有事务依次按照顺序执行,这样事务之间就不可能存在干扰,该级别不会引起问题 MySQL默认使用REPEATABLE-READ的级别
MySQL中的锁机制
MySQL锁的类型
从锁的类型上
- 共享锁 又叫读锁,在用户读数据的时候,对数据加锁
- 排他锁 又叫写锁,当用户写数据的时候,读数据加锁 从锁的级别上
- 行级锁 行级锁是粒度最小的锁,只针对当前操作的行进行加锁
- 表级锁 表级锁是粒度最大的锁,对当前操作的表进行加锁
- 页级锁 粒度在行级锁和表级锁中间,锁定当前行相邻的一组数据
MySQL中的乐观锁和悲观锁
- 悲观锁 select查询的在结果置顶for update;
//核心SQL,主要靠for update select status from t_goods where id=1 for update;
- 乐观锁 update更新的时候使用版本号字段作为where条件
//核心SQL update table set x=x+1, version=version+1 where id=#{id} and version=#{version};
MySQL使用注意点
int(10)和int(20)的区别
- 存储上: 数据的大小长度范围上没有区别
- 显示上: 如果指定zerofill,会在高位补0
char(10)和char(20)区别
- 存储上: 只存放定长10的字符,不足会在高位补充空格,超过报错
varchar(10)和varchar(20)区别
- 存储上: 往里面放多少个就是多少个,操作报错
网络
计算机网络体系结构
OSI七层结构
- 物理层 将数据以流的形式传输
- 数据链路层 将数据封装成帧,以数据帧的形式传输
- 网络层 将数据以包的形式传输
- 传输层 TCP\UDP 定义传输协议和端口,决定数据的传输快慢,准确
- 会话层 SMTP\DNS 解决节点之间连接问题
- 表示层 确保系统的应用层发送消息能被另一个系统应用层读取
- 应用层 HTTP\FTP解决文件传输,邮件传输
TCP\IP四层概念
- 数据链路层 解决硬件和软件之间的物理线路
- 网络层 将数据以包的形式在两个节点之间传输,就是我们通常说的IP协议
- 传输层 建立主机端到端之间的链接 TCP\UDP就是在这一层 端口号就是所谓的
端
- 应用层 最靠近用户的一层,为计算机用户提供接口,HTTP/FTP等
TCP,UDP的区别
- TCP是面向连接的点对点通信,发送数据之前建立连接,TCP提供可靠的通信,不会丢失,不会重复,按照循序到达,类似打电话
- UDP 是一对多的通信,无连接的,没有可靠性,类似广播
运行在TCP和UDP的应用层协议
- 运行在TCP上的协议 HTTP,HTTPS,FTP,SMTP,SSH
- 运行在UDP上的协议 DHCP,NTP(网络时间协议)
- 两者都有的协议 DNS,DHCP,ARP
浏览器输入url地址到页面返回的过程
- 从浏览器本地DNS缓存,系统DNS缓存,系统host文件,本地DNS服务器中解析url的具体IP地址
- 获取IP地址之后,建立连接,发起三次握手
- 建立TCP\IP连接之后,浏览器向服务器发送HTTP请求
- 服务器根据请求处理逻辑,返回具体的HTML内容
- 浏览器解析并且渲染HTML页面
TCP的三次握手
简单来说
- 客户端发送序列号
- 服务端发送序列号,确认帧
- 客户端发送确认帧 三次握手要确认的是 客户端发送正常,服务端接收正常,服务端发送正常,客户端接收正常
TCP的四次挥手
简单来说
- 客户端发送消息 我要关闭连接
- 服务端返回消息 我知道你要关闭连接
- 服务端返回消息 我也要关闭连接
- 客户端发送消息 我知道你要关闭连接 四次挥手的要确认的 双方都没有要发送的消息,服务端拆分为两次的目的是因为确认客户端关闭连接和自己的消息发送完毕要关闭连接之间存在时间间隔
什么是对称加密,非对称加密
- 对称加密是指加密和解密使用同一密钥,
- 非对称加密是加密和解密使用公钥和私钥两种密钥
cookie和session的区别
- cookie是放在客户端上的,安全性较差,单个cookie的数据不能操作4K
- session是放在服务器上的,大小无限制 由于HTTP协议是无状态的,所以需要一个值去判断用户身份