在二进制系统中无法精确的表示1/10
所以浮点数不适用于无法接受舍弃误差的金融计算中,应使用BigDecimal
类型
本质:
0.5能够表示,因为它可以表示成为1/2
0.75也能够表示,因为它可以表示成为1/2+1/(2^2)
0.875也能够表示,因为它可以表示成为1/2+1/(22)+1/(23)
0.9375也能够表示,因为它可以表示成为1/2+1/(22)+1/(23)+1/(2^4)
但是0.1不能够精确表示,因为它不能表示成为1/(2^n)的和的形式
重写发生在子类继承父类时,对父类的方法进行的改写操作
重载是发生在一个类中,相同的函数名,但是有不同的参数,参数的个数不一样,参数的位置不一样,参数的类型不同,这就是重载,常见的有构造函数,有参构造和无参构造。
相同点:一旦执行方法,可以使当前线程进入等待状态
区别:
声明位置不同,sleep在Threa类中,wait在Object类中
sleep必须指定睡眠时间,wait不要求
sleep可在任何地方使用,wait只能在同步方法代码块中使用
sleep中没有释放锁,而wait释放锁,导致其他线程可以使用同步控制块
sleep必须捕获异常,而wait不用
注:
注:放在HashMap中的key的元素需要同时重写hashCode和equals方法
hash冲突:键(key)通过hash函数得到的结果作为地址去存放键值对(key-value)(这就是hashmap的存值方式),当时计算发现这个地址已经存放了键值对,就会产生冲突,这就是hash冲突。
解决方法:
**1、开放定址法:**当冲突发生时,通过某种探测技术在散列表上形成一个探测序列,沿着此序列逐个查找,直到找出一个开放的地址。常见的探测技术有:线性探测、再平方探测、伪随机探测
**2、再哈希法:**对冲突的哈希值再次进行哈希处理,直到没有哈希冲突。
**3、链地址法:**将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针放在哈希表的第i个单元中。链地址法适用于经常进行插入和删除的情况。
**4、建立公共溢出区:**将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,都放入溢出表。
ArrayList基于数组,对随机index的访问比较快,空间浪费体现在列表结尾的预留空间
LinkList基于链表,增加和删除元素比较快,空间浪费体现在每个元素都需要消耗一定的空间
Vector是线程安全的类,而ArrayList是非线程安全的。
HashMap可以存放null
HashTable不能存放null
HashMap不是线程安全的类
HashTable是线程安全的类
StringBuffer 是线程安全的
StringBuilder 是非线程安全的
synchronized关键字是用来控制线程同步的
synchronized的使用
public class testThead implements Runnable {
int count = 0;
@Override
public void run() {
synchronized (this) {
System.out.println(count++);
}
}
}
testThead的调用
testThead thead = new testThead();
thead.run();
thead.run();
synchronized关键字的作用:
synchronized关键字可以把任何一个非null的对象作为”锁“,在JVM中,锁也叫对象监听器(Object Monitor)
实现原理
Java对象在内存中的布局主要由:对象头、实例数据和填充对齐组成。而synchronized的锁就存放在对象头中。
synchronized只能实现重量级的锁,而在Java中,重量级的锁是由Monitor(监听器)对象实现的。
wait()、notify()方法需要再同步代码块中执行的原因:因为这些方法需要调用ObjectMonitor(对象监听器)对象的内部方法来完成。
总结:synchronized实现重量级的锁,采用Monitor实现,通过操作Monitor中的ower、recursions的值来实现线程调用的成功与失败。
参考链接:https://zhuanlan.zhihu.com/p/377423211
java的多线程体现在:
导致原因:
解决方法:
1、保证内存可见性
可见性:一个线程对共享变量的修改,其他线程能够马上看到
实现原理
(1)当对非volatile变量进行读写时,每个线程先从主内存拷贝到CPU缓存中,如果计算机有多个CPU,每个线程可能在不同的CPU上被处理,这意味着每个线程可以拷贝到不同的CPU cache中,也就不能保证可见性。
(2)volatile变量不会被缓存到寄存器或者其他处理器不可见的地方,保证了每次读写变量都是从主内存中读,跳过了CPU cache这一步。所以一个线程改变了这个变量的值,其他线程能够马上看到。
2、禁止指令重排
(1)指令重排是JVM为了优化指令、提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度。
(2)指令重排包括编译器重排序和运行时重排序。
(3)volatile变量禁止指令重排序。针对volatile变量,在读写操作指令前后会插入内存屏障,指令重排序时不能把后面的指令重排序到内存屏障前,这样避免了多线程环境下程序乱序执行的情况。
synchronized可以保证并发编程的三大特性:原子性、可见性、有序性,而volatile只能保证可见性和有序性,不能保证原子性,也被称为轻量级的synchronize。
可重入锁:当线程获取某个锁后,还可以继续获取它,可以递归调用,而不会发生死锁
不可重入锁:与可重入相反,获取锁之后不能重复获取,否则会死锁
非公平的锁:获取锁的时候,并不是根据所申请的时间先后给等待的线程分配锁
相同点:
不同点:
java.nio全称java non-blocking IO,是指jdk1.4 及以上版本里提供的新api(New IO) ,为所有的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它可以提供非阻塞式的高伸缩性网络。
NIO在JDK1.4引入的,弥补了IO的不足,提供了高速的、面向块的IO
NIO核心组件:
NIO和IO的区别:
底层原理
加分部分:
Buffer有3中属性:
在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。
那么我们如何解决这两个问题呢?针对第一个问题,sso登录以后,可以将Cookie的域设置为顶域,即.a.com,这样所有子域的系统都可以访问到顶域的Cookie。我们在设置Cookie时,只能设置顶域和自己的域,不能设置其他的域。比如:我们不能在自己的系统中给baidu.com的域设置Cookie。
Cookie的问题解决了,我们再来看看session的问题。我们在sso系统登录了,这时再访问app1,Cookie也带到了app1的服务端(Server),app1的服务端怎么找到这个Cookie对应的Session呢?这里就要把3个系统的Session共享,如图所示。共享Session的解决方案有很多,例如:Spring-Session。这样第2个问题也解决了。
同域下的单点登录就实现了,但这还不是真正的单点登录。
同域下的单点登录是巧用了Cookie顶域的特性。如果是不同域呢?不同域之间Cookie是不共享的,怎么办?
这里我们就要说一说CAS流程了,这个流程是单点登录的标准流程。
参考链接:https://developer.aliyun.com/article/636281#slide-2
假设用户自己写了一个String类,会加载不进内存
原因
基于JVM的双亲委派机制,类加载器收到了类加载的请求,会将这个请求委派给他的父类加载器。而只有父类加载器无法完成加载请求时,子类才会被加载。这样用户自定义的String的加载请求最终到达顶层BootStrap ClassLoader启动类加载器,启动类加载器加载的是系统中的String对象,而用户写的String对象不会被加载。
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class EqualsExample {
public static void main(String[] args) {
// 对象 1
Persion p1 = new Persion();
p1.setName("Java");
p1.setAge(18);
// 对象 2
Persion p2 = new Persion();
p2.setName("Java");
p2.setAge(18);
// 创建 Set 集合
Set<Persion> set = new HashSet<Persion>();
set.add(p1);
set.add(p2);
// 打印 Set 中的所有数据
set.forEach(p -> {
System.out.println(p);
});
}
}
class Persion {
private String name;
private int age;
// 只重写了 equals 方法
@Override
public boolean equals(Object o) {
if (this == o) return true; // 引用相等返回 true
// 如果等于 null,或者对象类型不同返回 false
if (o == null || getClass() != o.getClass()) return false;
// 强转为自定义 Persion 类型
Persion persion = (Persion) o;
// 如果 age 和 name 都相等,就返回 true
return age == persion.age &&
Objects.equals(name, persion.name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Persion{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true; // 引用相等返回 true
// 如果等于 null,或者对象类型不同返回 false
if (o == null || getClass() != o.getClass()) return false;
// 强转为自定义 Persion 类型
Persion persion = (Persion) o;
// 如果 age 和 name 都相等,就返回 true
return age == persion.age &&
Objects.equals(name, persion.name);
}
@Override
public int hashCode() {
// 对比 name 和 age 是否相等
return Objects.hash(name, age);
}
同一个接口,使用不同的实例会执行不同的方法。
实现多态的三个条件:
好处:
绑定:通俗的讲绑定就是指你调用的方法是绑定在哪个类上的。例如父类 Father 有 方法 run();子类 Son 也有方法 run();那么你调用的是哪个类的方法?
静态绑定:程序编译期间的绑定(编译成.class文件就完成了绑定),java中的方法被final、static、private修饰的和构造方法都是静态绑定。
动态绑定:在程序中根据对象的类型进行绑定
在jdk1.2之前
若一个对象不被任何变量引用,那么程序就无法再使用这个对象,也就是说对象处于可触及状态(reachable),程序才能够使用它。如果reachable类型的数据存储的是另一块内存的地址,那么就称这块内存代表一个引用。
在jdk1.2之后
对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、虚引用(引用强度逐渐减弱)
1、强引用(StrongReference)
我们使用的引用大部分都是强引用。
如果一个对象具有强引用,那么垃圾回收器一定不会回收它。当内存空间不足时,JVM宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会随意回收具有强引用的对象来解决内存不足的问题。
2、软引用(SoftReference)
可有可无的,如果内存空间不足,垃圾回收器就会回收这些对象的内存。软引用可以用来实现内存敏感的高速缓存。
3、弱引用
也是可有可无的。
软引用和弱引用的区别在于:
具有弱引用的对象拥有更加短暂的生命周期。在垃圾回收器线程扫描它所管辖的区域时,只要发现了具有弱引用的对象,不管内存空间是否充足,都会回收它的内存。不过由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现具有弱引用的对象。
4、虚引用(PhantomReference)
CMS(Concurrent Mark Sweep)是一种以获取最短回收停顿时间为目标的收集器。
CMS是基于“标记-清除”算法实现的。
CMS的工作流程分为7步:
1、初始标记
2、并发标记
3、预清理
4、可被终止的预清理
5、重新标记
6、并发清理
7、并发重置
初始标记
暂停所有的其他线程,并记录下直接与root相连的对象
谈谈SpringBoot和Spring的关系,核心功能、优点
SpringBoot是为了简化Spring应用的初始化搭建而诞生的。该框架使用了特定的方式来进行配置。
Spring Boot本身并不提供Spring的核心功能,而是作为Spring框架的脚手架框架,以达到快速构建项目,预置第三方配置,开箱即用的目的。
SpringBoot基于Spring4.0设计,本质上来说SpringBoot就是Spring,它帮你完成了一些bean的配置
核心功能:
优点:
在SSM框架下,我们通常会分为四层:DAO层(MyBatis模块)、Service层(Spring模块)、Controller层(SpringMVC模块)、View层(SpringMVC模块)
DAO层主要负责与数据库进行交互设计,用来处理数据的持久化操作。
在DAO里面我们首先要设计DAO的接口,然后需要编写对应接口的xml配置文件(具体的sql语句就写在xml中)
Service层主要负责具体业务的逻辑设计
同样的,首选需要设计Service的接口以及实现类,然后在实现类中调用DAO层的接口,来完成业务的具体实现。
Controller层主要负责业务的流程控制
Controller层通过调用Service层的接口来控制业务流程,比如页面的转发,重定向等操作。
View层主要负责前台页面的展示
需要与Controller结合起来进行开发,Jsp发送请求,Controller接收请求,处理,返回,Jsp回显数据
三层之间的联系
IOC(DI)控制反转,一种通过描述(XML或注解)并通过第三方生产或获得特定对象的方式。
AOP面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
【得分点】
与Spring的关系 、核心功能 、优点
【参考答案】
标准回答
Spring Boot本身并不提供Spring的核心功能,而是作为Spring框架的脚手架框架,以达到快速构建项目,预置第三方配置,开箱即用的目的。
Spring Boot 核心功能:
1. 自动配置:针对很多Spring应用程序常见的应用功能,Spring Boot能自动提供相关配置。
2. 起步依赖:Spring Boot通过起步依赖为项目的依赖管理提供帮助。起步依赖其实就是特殊的Maven依赖和Gradle依赖,利用了传递依赖解析,把常用库聚合在一起,组成了几个为特定功能而定制的依赖。
3. 端点监控:Spring Boot 可以对正在运行的项目提供监控。
Spring Boot 优点如下:
· 可以快速构建项目;
· 可以对主流开发框架的无配置集成;
· 项目可独立运行,无需外部依赖Servlet容器;
· 提供运行时的应用监控;
· 可以极大地提高开发、部署效率;
· 可以与云计算天然集成。
加分回答
其实从本质上来说,Spring Boot就是Spring,它帮你完成了一些Spring Bean配置。Spring Boot使用“习惯优于配置”的理念让你的项目快速地运行起来,使用Spring Boot能很快的创建一个能独立运行、准生产级别、基于Spring框架的项目。
【延伸阅读】
Spring Boot常用注解:
1. @SpringBootApplication
在Spring Boot入口类中,唯一的一个注解就是@SpringBootApplication。它是Spring Boot项目的核心注解,用于开启自动配置,准确说是通过该注解内组合的@EnableAutoConfiguration开启了自动配置。
2. @EnableAutoConfiguration
@EnableAutoConfiguration的主要功能是启动Spring应用程序上下文时进行自动配置,它会尝试猜测并配置项目可能需要的Bean。自动配置通常是基于项目classpath中引入的类和已定义的Bean来实现的。在此过程中,被自动配置的组件来自项目自身和项目依赖的jar包中。
3. @Import
@EnableAutoConfiguration的关键功能是通过@Import注解导入的ImportSelector来完成的。从源代码得知@Import(AutoConfigurationImportSelector.class)是@EnableAutoConfiguration注解的组成部分,也是自动配置功能的核心实现者。
4. @Conditional
@Conditional注解是由Spring 4.0版本引入的新特性,可根据是否满足指定的条件来决定是否进行Bean的实例化及装配,比如,设定当类路径下包含某个jar包的时候才会对注解的类进行实例化操作。总之,就是根据一些特定条件来控制Bean实例化的行为。
5. @Conditional衍生注解
在Spring Boot的autoconfigure项目中提供了各类基于@Conditional注解的衍生注解,它们适用不同的场景并提供了不同的功能。通过阅读这些注解的源码,你会发现它们其实都组合了@Conditional注解,不同之处是它们在注解中指定的条件(Condition)不同。常见的衍生注解如下:
o @ConditionalOnBean:在容器中有指定Bean的条件下。
o @ConditionalOnClass:在classpath类路径下有指定类的条件下。
o @ConditionalOnMissingBean:当容器里没有指定Bean的条件时。
o @ConditionalOnMissingClass:当类路径下没有指定类的条件时。
其中第3、4步为初始化前执行,5~6步为初始化操作,第7步在初始化后执行
延迟加载:在真正的使用数据时才发起查询,不用的时候不用查,按需查询(懒加载)
延迟加载参数配置:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
settings>
一对一的查询:
<association property="user" column="uid" javaType="com.mybatis.domain.User" select="com.mybatis.dao.IUserDao.findById"></association>
一对多的查询:
<collection property="accounts" ofType="com.mybatis.domain.Account" select="com.mybatis.dao.IAccountDao.findAccountByUid" column="id">collection>
后端解析前端的请求,封装request和response对象,创建servlet对象,调用service方法。
进程是CPU资源分配的最小单位,线程是CPU调度的基本单位
可以比喻为:进程=火车,线程=车厢
一个进程包含多个线程,一个线程只能在一个进程中,每个进程至少包含一个线程
同一进程的多个线程之间共享地址空间,进程之间是独立的地址空间
(1)基于内存的方式
**1、匿名管道:**主要用于父子进程之间的通信
2、信号量
信号是一种比较复杂的通信方式,信号产生的条件:按键、硬件异常等等
信号量是一个计数器,可以控制多个进程对共享资源的访问。它通常作为一种锁机制,防止某进程在访问共享资源时,其他进程也访问该资源。因此,主要用于进程间以及同一进程不同线程之间的同步手段。
**3、消息队列:**消息队列是消息的链表,消息队列克服了信号传递信息系少,管道只能承载无格式字节流以及缓冲区大小受限等特点。消息队列起信箱作用,提供了一种在两个不相关进程间传递数据的简单方法。
**4、共享内存:**共享内存就是映射一段能够被其他进程所访问的内存,该内存由一个进程创建,但多个进程可以访问。
(2)基于磁盘的方式
1、文件
2、命名管道
(3)基于网络的方式
1、socket(套接字)
2、消息队列
3、RPC
4、HTTP等网络协议
僵尸进程是指终止但未被回收的进程。如果一个进程已经终止,但是它的父进程尚未调用wait()或wait()对它进行清理,这是的进程状态称为僵死状态,处于僵死状态的进程称为僵尸进程。
产生原因:
当一个进程(init除外)在exit()时,并不会马上被内核清除,而是进程会处在一种"已终止"的状态,并留下一个称为“僵尸进程”的数据结构,等待父进程的回收处理。
解决办法:
当出现僵尸进程时,我们无法通过kill命令清除。但是我们可以杀死它的父进程,让它变成孤儿进程,并进一步被系统中管理孤儿进程的进程收养并处理。
孤儿进程: 其父进程执行完成或被终止后仍继续运行的一类进程。
维度 | 多进程 | 多线程 | 总结 |
---|---|---|---|
数据共享、同步 | 数据是分开的:共享复杂,需要用IPC;同步简单 | 多线程共享进程数据:共享简单;同步复杂 | 各有优势 |
内存、CPU | 占用内存多,切换复杂,CPU利用率低 | 占用内存少,切换简单,CPU利用率高 | 线程占优 |
创建销毁、切换 | 创建销毁、切换复杂,速度慢 | 创建销毁、切换简单,速度快 | 线程占优 |
编程调试 | 编程简单,调试简单 | 编程复杂,调试复杂 | 进程占优 |
可靠性 | 进程间不会相互影响 | 一个线程挂掉将导致整个进程挂掉 | 进程占优 |
分布式 | 适应于多核、多机分布 ;如果一台机器不够,扩展到多台机器比较简单 | 适应于多核分布 | 进程占优 |
多进程模式的优势是CPU,适用于CPU密集性的工作场景(nginx、chrome浏览器、redis、大部分web server)
多线程模型的主要优势为线程之间切换代价小,适用于IO密集型的工作场景,同时也适用于单机多核分布式场景(桌面软件等等)
线程的生命周期包含5个阶段:新建、就绪、运行、阻塞、死亡
新建:刚使用new方法,创建了一个新的线程
就绪:调用线程的start方法后,线程等待cpu的调用
运行:获得了cpu的时间片,正在执行程序代码
阻塞:由于某些原因,放弃了cpu的使用权,如sleep()、wait(),他们需要等待cpu分配资源才能进入运行状态
死亡:main方法结束了,线程被销毁,不可复生
代码解释:
public class threadDemo {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(); //新建状态
t.start(); //就绪状态,等待cpu选中调用
//当线程选中后,线程进入运行状态
t.wait(); //阻塞状态,线程放弃cpu使用权,当cpu分配资源可唤醒
}
//main方法结束,线程销毁
}
1、最优页面置换算法(OPT)
2、先进先出置换算法(FIFO)
实现方式
把调入内存的页面根据调入顺序排成一个队列,需要换出页面时选择队头
3、最近最久未使用算法(LRU)
4、最不常用算法(LFU)
计算机向缓冲区填写数据时超出了缓冲区本身的容量,溢出的数据覆盖在合法的数据上。
危害:
原因:程序中没有仔细检查用户的输入
OSI七层模型从上到下依次是:
STMP邮件协议、FTP文件传输协议、DNS域名解析协议、HTTP超文本传输协议 将超文本从服务器传输到本地服务器
数据格式的转换、对图片的编解码、数据的压缩与加密解密
保持两个网络节点之间的通信,包括通信链接的建立、维护通信链接的通畅,决定了通信是否中断,以及中断时从何处重新发送通信请求
是最重要的一层,可以对传输进行流量控制,如果数据包过大,可以将数据包分解
路由器,将网络地址翻译成对应的物理地址
交换机,控制物理层和网络层之间的通讯,将网络层的数据包封装成帧,传输给物理层
电缆连接、网卡等,为数据链路层提供物理连接,根据它们的机械、电气、功能和过程特性,规定了电缆和接头的类型,传送信号的电平电压等。
TCP五层模型相比于OSI七层模型,将OSI七层模型的应用层、表示层、会话层合并为一层:应用层,其他不变。
TCP | UDP |
---|---|
面向有连接 | 面向无连接 |
可靠的 | 不可靠的 |
基于字节流 | 基于报文 |
点对点 | 一对一、一对多、多对一、多对多 |
工作效率低 | 工作效率高,适用于高速传输或广播通信 |
资源要求多 | 资源要求低 |
对称加密算法:RSA,DSA/DSS
特点是文件加密和解密使用相同的密钥加密;对称加密算法使用起来简单快捷,密钥较短,且破译困难,解密速度快。除了数据加密标准(DES),另一个对称密钥加密系统是国际数据加密算法(IDEA),它比DES的加密性好,而且对计算机功能要求也没有那么高。
非对称加密:RSA,DSA/DSS
需要两个密钥:公开密钥和私有密钥;公开密钥与私有密钥是一对。如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。主要是用来保护传输客户端生成的用于对称加密的随机数私钥
三次握手
第一次握手
客户端发送一个SYN标记包,发送完之后,客户端进入SYN-SEND状态
第二次握手
服务端同意连接,给客户端发送SYN+ACK标记包,服务端进入SYN-RCVD状态
第三次握手
客户端收到之后,给服务端发送ACK包,连接建立,两者都进入ESTABLISHED状态
为了防止已经失效的请求报文突然有传到服务端,引起错误
如果客户端一开始发送的那个连接由于网络堵塞等原因导致请求超时,并且放弃了连接。等网络回复正常后,服务端会收到那个已经超时的连接请求。如果时两次握手就可以建立连接,那么此时对于服务端来说连接时成功的。但是此时客户端已经放弃了连接,这就造成了错误。
所以,综上,三次握手是为了保证在不可靠的信道上进行可靠的连接所创建的。
所以三次握手本质上是为了解决在不可靠的信道上进行可靠连接
通俗解释:
A:我爱你
B:我也爱你
A:那我们在一起吧
四次挥手
第一次挥手
客户端发送FIN标记包给服务端,表示要关闭连接,客户端进入终止等待1状态(FIN-WAIT-1)
第二次挥手
服务端接到FIN包,发送ACK标记包给客户端,服务端进入关闭等待状态(CLOSE-WAIT),客户端进入终止等待2状态(FIN-WAIT-2),此时客户端可以接收数据,服务端可以发送数据
第三次挥手
服务端发送FIN包,进入最后确认状态(LAST-ACK)
第四次挥手
客户端收到后发送ACK标记包给服务端,客户端进入超时等待状态(TIME-WAIT),经过超时时间后关闭连接,服务端收到ACK后马上关闭连接
为什么需要等待2倍最大报文生存时间之后再关闭连接,原因有两个:
这是为了保证对方已收到了ACK包,假设客户端发送ACK包后马上关闭连接,如果ACK包在网络中丢失,那么服务端将永远停留在最后确认状态
如果客户端发送ACK包后,服务端没有收到,服务端会重发FIN包,客户端响应FIN包,重发ACK包,并刷新超时时间
通俗解释:
A:分手吧
B:好吧
B:是我哪里做的不对吗?
A:是的,我们分手吧(此时B收到信息后直接就断了念想,而A却在等待B的挽回,过一段时间后A也就直接拉黑删除了对方)
**DNS协议(应用层):**将域名解析为IP,输入域名时,将其发送给“DNS服务器”,就会返回对应的IP。本机在解析域名时,首先会在host文件中查找,如果查到就直接使用,否则就发送给DNS服务器。
TCP/IP协议:IP协议用来寻找地址(即传输数据的目标节点)对应网络层;TCP协议用来规范传输规则的,对应传输层。IP负责找地址,TCP负责具体的传输。TCP/IP是一套规则,并不能具体工作,就像程序中的接口一样,而Socket是TCP/IP协议的一个具体实现。
**HTTP协议:**应用层协议,在TCP/IP协议接收到数据之后,需要通过HTTP协议来解析。HTTP报文分为请求报文和响应报文,都包括三部分:首行、头部和主体。
200——服务器成功返回页面
304——(未修改)自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。
400——(错误请求)服务器不理解请求的语法
404——请求的网页不存在
500——(服务器内存错误)服务器遇到错误,无法完成请求
表示临时响应,需要请求者继续执行操作。
100——(继续)服务器表示已经收到请求的第一部分,正在等待请求者发送其他部分
101——(切换协议)请求者已要求服务器切换协议,服务器确认并准备切换
200——服务器成功处理并返回网页
201——(已创建)请求成功,服务器创建了新的资源
202——(已接收)服务器已接收请求,但没有处理
203——(非授权信息)服务器成功处理,但返回的信息可能来自其他源
204——(无内容),服务器成功处理请求,但没有返回任何内容
205(重置内容)服务器成功处理了请求,但没有返回任何内容。与 204 响应不同,此响应要求请求者重置文档视图(例如,清除表单内容以输入新内容)。
206(部分内容)服务器成功处理了部分 GET 请求。
要完成请求,需要进一步操作。通常,这些状态码用来重定向。Google 建议您在每次请求中使用重定向不要超过 5 次。您可以使用网站管理员工具查看一下 Googlebot 在抓取重定向网页时是否遇到问题。诊断下的网络抓取页列出了由于重定向错误导致 Googlebot 无法抓取的网址。
300(多种选择)针对请求,服务器可执行多种操作。服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。
301(永久移动)请求的网页已永久移动到新位置。服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。您应使用此代码告诉 Googlebot 某个网页或网站已永久移动到新位置。
302(临时移动)服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来响应以后的请求。此代码与响应 GET 和 HEAD 请求的 301 代码类似,会自动将请求者转到不同的位置,但您不应使用此代码来告诉 Googlebot 某个网页或网站已经移动,因为 Googlebot 会继续抓取原有位置并编制索引。
303(查看其他位置)请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。对于除 HEAD 之外的所有请求,服务器会自动转到其他位置。
304(未修改)自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。
如果网页自请求者上次请求后再也没有更改过,您应将服务器配置为返回此响应(称为 If-Modified-Since HTTP 标头)。服务器可以告诉 Googlebot 自从上次抓取后网页没有变更,进而节省带宽和开销。
.
305(使用代理)请求者只能使用代理访问请求的网页。如果服务器返回此响应,还表示请求者应使用代理。
307(临时重定向)服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来响应以后的请求。此代码与响应 GET 和 HEAD 请求的 301 代码类似,会自动将请求者转到不同的位置,但您不应使用此代码来告诉 Googlebot 某个页面或网站已经移动,因为 Googlebot 会继续抓取原有位置并编制索引。
这些状态码表示请求可能出错,妨碍了服务器的处理。
400(错误请求)服务器不理解请求的语法。
401(未授权)请求要求身份验证。对于登录后请求的网页,服务器可能返回此响应。
403(禁止)服务器拒绝请求。如果您在 Googlebot 尝试抓取您网站上的有效网页时看到此状态码(您可以在 Google 网站管理员工具诊断下的网络抓取页面上看到此信息),可能是您的服务器或主机拒绝了 Googlebot 访问。
404(未找到)服务器找不到请求的网页。例如,对于服务器上不存在的网页经常会返回此代码。
如果您的网站上没有 robots.txt 文件,而您在 Google 网站管理员工具"诊断"标签的 robots.txt 页上看到此状态码,则这是正确的状态码。但是,如果您有 robots.txt 文件而又看到此状态码,则说明您的 robots.txt 文件可能命名错误或位于错误的位置(该文件应当位于顶级域,名为 robots.txt)。
如果对于 Googlebot 抓取的网址看到此状态码(在"诊断"标签的 HTTP 错误页面上),则表示 Googlebot 跟随的可能是另一个页面的无效链接(是旧链接或输入有误的链接)。
405(方法禁用)禁用请求中指定的方法。
406(不接受)无法使用请求的内容特性响应请求的网页。
407(需要代理授权)此状态码与 401(未授权)类似,但指定请求者应当授权使用代理。如果服务器返回此响应,还表示请求者应当使用代理。
408(请求超时)服务器等候请求时发生超时。
409(冲突)服务器在完成请求时发生冲突。服务器必须在响应中包含有关冲突的信息。服务器在响应与前一个请求相冲突的 PUT 请求时可能会返回此代码,以及两个请求的差异列表。
410(已删除)如果请求的资源已永久删除,服务器就会返回此响应。该代码与 404(未找到)代码类似,但在资源以前存在而现在不存在的情况下,有时会用来替代 404 代码。如果资源已永久移动,您应使用 301 指定资源的新位置。
411(需要有效长度)服务器不接受不含有效内容长度标头字段的请求。
412(未满足前提条件)服务器未满足请求者在请求中设置的其中一个前提条件。
413(请求实体过大)服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。
414(请求的 URI 过长)请求的 URI(通常为网址)过长,服务器无法处理。
415(不支持的媒体类型)请求的格式不受请求页面的支持。
416(请求范围不符合要求)如果页面无法提供请求的范围,则服务器会返回此状态码。
417(未满足期望值)服务器未满足"期望"请求标头字段的要求。
这些状态码表示服务器在处理请求时发生内部错误。这些错误可能是服务器本身的错误,而不是请求出错。
500(服务器内部错误)服务器遇到错误,无法完成请求。
501(尚未实施)服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码。
502(错误网关)服务器作为网关或代理,从上游服务器收到无效响应。
503(服务不可用)服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。
504(网关超时)服务器作为网关或代理,但是没有及时从上游服务器收到请求。
505(HTTP 版本不受支持)服务器不支持请求中所用的 HTTP 协议版本。
close-wait状态出现在TCP第二次挥手时,服务端给客户端发送ACK数据包后,服务端进入关闭等待状态(close-wait)
time-wait状态出现在TCP第四次挥手时,客户端给服务端发送ACK数据包后,客户端进入超时等待状态(time-wait)
这是为了保证对方已经收到了ACK包,假设客户端发送ACK包后马上关闭连接,如果ACK包在网络中丢失,那么服务端将永远停留在最后确认状态。
如果客户端发送ACK包后,服务端没有收到,服务端会重发FIN包,客户端响应FIN包,重发ACK包,并刷新超时时间。
1、检验和:在数据传输过程中,将发送的数据作为一个16位的整数,将这些整数加起来,并且前面的进位不要丢器,补在后面,最后取反,得到检验和。
发送方:在发送数据之前计算检验和,并进行校验和的填充。
接收方:收到数据后,对数据以同样的方式进行计算,求出校验和,与发送方的进行比对。
注意:如果接收方比对校验和与发送方不一致,那么数据一定传输有误。但是如果接收方比对校验和与发送方一致,数据不一定传输成功。
2、确认应答和序列号:
序列号:TCP传输时给每个字节的数据都进行了编号,这就是序列号。
确认应答:TCP传输过程中,每次接收放接收到数据后,都会对传输方进行确认应答,也就是发送ACK报文。这个ACK报文中中会带有对应的确认序列号,也就是告诉发送方,就收到了哪些数据,下一次的数据该从哪里发。
3、超时重传
如果发送方发送数据一段时间后没有收到ACK包,就会重新发送数据。
4、连接管理
TCP通过三次握手,四次挥手保证连接的可靠性。
5、流量控制
接收端使用TCP报文的窗口字段,来控制发送方的发送速率。
6、拥塞控制
TCP通过维护一个拥塞窗口来进行拥塞控制,通过检测网络的状态来调整拥塞窗口的大小,目的是防止过多的数据注入到网络,引起路由器过载。
其他方式:
7、应用数据被分割成TCP认为最合适发送的数据块。
8、TCP给每个需要发送的包编号,接收方对数据包进行排序,有序将数据传送给应用层。
9、TCP接收端会丢弃重复的数据
目的
方式
区别
其实真正的发送窗口只有一个,发送窗口选取的是接收窗口和拥塞窗口中较小的一个。比如网络状况较好,传输速率为10G/S,那么网络拥塞这一因素就不是主要的影响,主要看接收方的缓冲区大小。相反,如果链路的传输速率只有10M/S,又有大量的节点在传输,此时拥塞控制就成了主要因素。
流量控制是点对点的通信量控制,拥塞控制是考虑了全局的网络负荷,是一个全局性的概念。
**浏览器缓存(Brower Caching)**是浏览器对之前请求过的文件进行缓存,以便下一次进行重复利用,节省带宽,提升访问速度,减轻服务器压力。
http缓存机制主要在http响应头实现,响应头相关字段有Expires、Cache-Control、Last-Modified、Etag。
第一次请求:
第二次请求相同网页:
强缓存
200 form memory cache:不访问服务器,一般已经加载了该资源在内存中,直接从内存中读取缓存。浏览器关闭后,数据将不存在。
200 from disk cache:不访问服务器,已经在之前某个时间访问了该资源,直接从硬盘中读取缓存。关闭浏览器后,数据依旧存在。
优先访问 memory cache ,其次是 disk cache ,最后是请求网络资源。
协商缓存
向服务器发起请求,服务器根据请求的request header的一些参数,来判断是否命中协商缓存,如果命中,则返回304并带上新的reponse header并通知浏览器从缓存中读取资源。
总结: 强制缓存根据过期时间来使用,而协商缓存根据文件是否被修改来使用,如果过期了就需要通过协商缓存来确定文件有没有被修改,如果修改了就请求服务器返回修改后的文件,如果没有修改就直接使用缓存中的资源。
B-树有如下特点:
B+树是B-树的变体,也是一种多路搜索树,它与B-树不同之处在于:
因为内节点并不存储 data,所以一般B+树的叶节点和内节点大小不同,而B-树的每个节点大小一般是相同的,为一页。
使用B+树而不是B树的原因
B树必须用中序遍历的方法按序扫库,而B+树直接从叶子节点挨个遍历就行。B+树支持range-query(范围搜索),而B树不支持,这也是数据库选用B+树的主要原因。
B+树节点大小更小,一次IO读入的节点数更多 ,磁盘读写代价更低
任何关键字的查找都必须走一条从根节点到叶子节点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
使用B+树而不是AVL树、红黑树的原因
事务是指逻辑上的一组操作,要么都执行,要么都不执行
事务具有四大特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
利用Innodb的 undo log(回滚日志),是实现原子性的关键,它保存了事务发生前的数据的一个版本,可以将数据回滚到修改前的样子。
从两个层面上来说:
数据库通过原子性、隔离性、持久性来保证一致性。也就是说,四大特性中C(一致性)是目的,而A(原子性)、I(隔离性)、D(持久性)是手段。数据库必须实现AID三大原则,才有可能实现一致性。
通过代码判断数据库数据是否有效,然后决定回滚还是提交数据!
利用锁和MVCC机制,MVCC即多版本并发控制(Multi Version Concurrency Control)
利用Innodb的 redo log(重做日志)。
正如之前所说,mysql先将磁盘上的数据加载到内存,在内存中对数据进行修改,再刷回磁盘。如果在此时突然宕机,内存上的数据就会丢失,这就不能保证持久性。
但是如果在事务提交之后直接写入到磁盘,会非常的消耗资源,而且事务的SQL涉及多个数据表的修改时,效率会非常的慢。
所以引入了redo log解决这个问题,当数据修改时,不仅在内存中操作,而且在redo log中记录这次操作。当事务提交时,会将redo log进行刷盘(redo log一部分在内存中,一部分在磁盘上)。当数据库宕机时,会将redo log的内容恢复到数据库,再根据redo log(重做日志)和binlog(归档日志)内容决定回滚数据还是提交数据。
MySQL的事务回滚是通过回滚日志(undo log)实现的,所有事务进行的修改都会先记录到回滚日志中,然后再对数据库进行对应的操作。
保证MySQL操作的原子性,事务中的操作要么全部执行,要么全部不执行。
数据库引擎:用于存储、处理、保护数据的核心服务。
常见的数据库引擎:Innodb 、 MyISAM 、MEMORY 、MERGE
MySQL存储引擎中,MylSAM和InnoDB的区别
InnoDB | MyISAM |
---|---|
支持事务 | 不支持事务 |
支持外键 | 不支持外键 |
聚集索引 | 非聚集索引 |
支持行锁、表锁 | 支持表锁 |
必须有主键 | 可以没有 |
不保存表的行数 | 保存了表的行数 |
在联合索引中,如果你的SQL语句用到了联合索引中的最左边的索引,那么这条SQL语句就可以利用这个联合索引进行匹配
例如:某表现有索引(a,b,c)
select * from t where a=1 and b=1; #这样可以利用到定义的索引(a,b,c),用上a,b
select * from t where a=1 and b=1; #这样可以利用到定义的索引(a,b,c),用上a,b
select * from t where a=1 and c=1; #这样可以利用到定义的索引(a,b,c),但只用上a索引,b,c索引用不到
数据库设计:
SQL语句优化:
MySQL常用的引擎是InnoDB,支持行锁和表锁
多个事务操作同一行数据时,后来的事务处于阻塞状态,后来的事务可以操作其他的行数据,解决了表锁高并发性能低的问题。
行锁的优点:锁的粒度小,发生锁冲突的概率低;处理并发的能力强
行锁的缺点:开销大;加锁慢;会出现死锁
常见的数据库id解决方案:
先着重介绍一下雪花算法,这是最常用的,也是重点。
**雪花算法:**snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。可以保证几乎全球唯一!
三种解决方案的优缺点:
解决方案 | 特点 | 优点 | 缺点 |
---|---|---|---|
数据库自增 | 数值类型,值递增,由数据库内部生成 | 最简单的方法,性能优秀 | 不适合分库分表场景,会出现主键冲突,ID重复 |
UUID | 字符串类型 | 1. 适用分库分表场景 2. UUID一般由Java代码生成,java不需要查询就能知道ID |
1. 性能不如自增 2. 可读性差,不能根据ID进行范围查询 |
雪花算法 | 数值类型,有Twitter提供的分布式ID算法,递增 | 1. 适用分库分表场景,就是为了这个场景而生的 2. 由Java代码生成,不需要查询就可以知道ID 3. 所有需要唯一ID的都可以适用雪花算法,如登录token、日志编号 |
1. 性能稍不如自增 2. 生成的值稍长,传递到前端number容易造成精度丢失,可转化成字符串解决 |
综上,使用雪花算法而不使用数据库自增,是为了适应分布式场景。
回溯是递归的一种表现形式
递归算法是为了描述问题的某一状态,必须用到上一个状态,循环调用方法本身,以此得到目标状态,强调的是状态本身。
回溯的本质是为了得到可能存在的所有情况,强调的是穷举所有可能的情况。
动态规划常常适用于有重叠子问题(比如子串)和最优子结构性质的问题。当我们需要优化递归时,也会用到动态规划。
动态规划的三个关键点:
什么时候使用动态规划?
重复子问题
最优子结构
优化递归
Java基础
设计模式
计算机网络
操作系统
数据库
Java多线程
JVM
测试代码