导图社区 JAVA_计算机基础
JAVA_计算机基础的思维导图,锁是一个名词,代表一个资源,如进入到synchronized(this) 代码后,表示线程持有锁,该锁就是当前对象。
编辑于2023-07-23 00:26:53 浙江省JAVA SE
Java介绍
JDK
开发工具
java.exe
运行java程序
javac.exe
编译工具
javaw.exe
运行GUI程序
JRE
java运行环境
核心类库
JVM
java虚拟机
JAVA EE
spring
Hibernate
struts
mybatis
MVC框架
各个框架对比和项目优化
JPA
EJB
设计模式
结构型模式
创建型
行为型
JAVA web
基础
tomcat服务器
JSP语法,EL 内置对象
Listener和filter
进阶
原理
HTTP
请求响应的架构原理
web容器
其他
UML
使用ProcessOn绘制UML图
类
类名
属性
+
public
-
private
#
protected
方法
场景题
前端技术
XML编程
JDBC编程
计算机基础
数据结构
链表
堆和栈
队列
数组
字符串
KMP算法
树
图
集合和搜索
顺序搜索
对半搜索
哈希/散列表
常用算法
遍历
排序
搜索
分治
动态规划
高级算法
各个排序算法的时间复杂度
操作系统
概述
进程的描述和控制
输入输出系统
存储器管理
处理机调度和死锁
java内存模型
用户态 内核态切换
计算机网络
概述
网络分层
运输层
TCP和UDP
协议
网络层
IP
ICMP
路由选择协议
应用层
域名系统DNS
电子邮件
计算机组成部分
数据库
redis
数据结构
持久化方式
事务
隔离级别
MVCC
索引
存储引擎
常用的SQL语句
框架
springbooot
mybatis
数据库
数据表
1.根据关系进行类的定义 (new一个子结构对象数组 静态初始化)
2.根据数据表,利用 引用 进行对象联系 (set方法将对象数组传入父结构)
3.根据关系获取数据 (get方法将父结构中的子结构对象数组的信息打印)
数据表结构的映射和继承的对比。映射转换的箭头指向谁,谁就是父结构,拥有子结构的对象数组
父结构和子结构是拥有关系
一个人拥有一辆车,一辆车的拥有者是一个人
父类和子类是属于关系
超人属于人,超人有人的方法
JAVA面向对象
JAVA语法基础
标识符
常量和变量
数据类型
基本数据类型
boolean
1bit
实际按1字节处理
byte
1字节
char
2字节
short
2字节
int
4字节
2^31次方
float
4字节
long
8字节
-2^63 2^63次方
double
8字节
引用数据类型(存放在堆中)
数组
基本数据类型
静态初始化
int a[]=int []{1,2,3}
动态初始化
int a[]=int [3]
a[1]=1
引用数据类型
静态初始化
Person p[]=Person []{new Person("a1",20) , new Person("b1",20) }
动态初始化
Person p[]=Person[3]
p[1]=new Person("1",1)
数组复制
深拷贝
栈内存和堆内存的地址及内容都复制了一份,变成两份
浅拷贝
栈内存还是只有一个引用地址,堆内存出现了两个对象
字符串
.equals()
定义
直接赋值
对象实例化
对象
运算符
/
除数和被除数只要有一个是小数形式 就全转化成小数形式
double a=1/3 ; ==0.0 double a=1.0/3 ; ==0.33
流程控制语句
Java特有的标签:
outer
break:outer
index: for (int i = 1; i for (int j = 0; j System.out.print(j); if(i==50&&j==10){ break index; } } System.out.println(); }
inner
break:inner
方法
返回值类型
基本数据类型
public 数据类型名 方法名 {return 该数据类型的值}
引用数据类型
public 类名称 方法名{return new 类名称}
即该类的值(对象)
参数
方法可变参数
public static int sum(int... data)
形参的本质是数组
调用 : 类名.sum(1,2,3)
注释
关键字
static
全局(静态)
使类,方法,属性变为全局
可以直接使用类名进行访问
描述一个变量为公共属性
static在java和c中都是一样的,生命周期最大,可以用来实现变量的保存
定义方法
非static定义的方法和static定义的方法都在同一个类时
static定义的方法不能“直接”调用非static的方法或属性
需要new 类名().方法名()去调用
非static定义的方法可以直接调用static的方法或属性
定义属性
在一个类中的static属性位于全局数据区,所有对象均可访问,且只有一个该属性
通过类名可直接调用static属性
final
常量
使类不能被继承
使方法不能被重写
使变量变为常量,不能被改变,只能赋值一次
权限
private
外部类要想访问private属性只有一种方法,写公共方法,return 该private属性
当private和static同时修饰一个变量,则该变量不能直接用类名进行访问,因为private将该变量进行封装处理
default
protect
public
面向对象三大特征
1.封装 [安全性] 包, 类, 方法, 属性, getter/setter, this 访问修饰符(private ,protected,public ) 方法修饰符 (static , final ,abstract) 方法重载 2.继承[重用性] 父类和子类, super, 方法重写, 接口 3.多态 [可扩展性和可维护性] 编译时类型和运行时类型不一致,就会出现所谓的多态 继承关系、实现关系 方法重写 里氏替换原则 编译类型和运行时类型
封装
包
类
基本数据类型 int 引用数据类型名String 你写的引用数据类型名Person 转化成“类型”的形式:String.class Person.class synchronized() 括号是引用数据类型,包括对象 类型名.class
内部类
内部类
定义内部类的好处
内部类访问外部类的私有成员
外部类也可以访问内部类的私有成员
内部类定义方式
1.用static
实例化
外部类.内部类 内部类对象=new 外部类.内部类()
2.不用static
实例化
外部类.内部类 内部类对象=new 外部类().new 内部类()
即必须先获取外部类实例化对象,再利用外部类对象去实例化内部类对象
内部类的使用
在外部类中,外部类可直接访问内部类私有成员属性
内部类对象.内部类属性
在内部类中,内部类访问外部类的私有成员属性
外部类.this.外部类属性
内部接口/内部抽象类
定义方式和定义内部类一样
在外部接口里面,可以定义内部接口/内部抽象类,甚至写内部类(用于实现该外部接口)
外部接口的实现和原来接口的实现一样
内部接口可以不实现,实现方法就是在该“实现外部接口的类”里面去“写一个实现内部接口的类"
在方法里面定义内部类
在外部类的方法里面
实例化
new 外部类().外部类的方法()
在外部接口的方法里面
?
匿名内部类
好处
方便对接口进行重写,不需要写类去实现接口,直接在新实例化的接口下方用大括号去重写接口
使用
接口名 对象名 =new 接口名(){ 重写的内容 }
等同于可以直接new接口了!
之后就可以随意调用该对象了
仅仅只是匿名内部类而已
new 接口名(){ 重写的内容 }
匿名内部类的匿名对象
抽象类
抽象类作为系统中多个子类的共同父类,它所体现的是一种模板式设计。抽象父类作为多个子类的 抽象父类,可以被当成系统实现过程中的中间产品,这个中间产品已经实现了系统的部分功能(那些已经提供实现的 方法),但这个产品依然不能当成最终产品,必须有更进一步的完善,这种完善可能有几种不同方式
内部
内部可以有成员属性,构造方法,普通抽象方法,普通方法, 抽象方法只需要声明,不要定义,且抽象方法必须可以被继承,不能被final修饰
普通抽象方法的定义
public abstract 数据类型 run();
在抽象类中,不写abstract修饰方法,默认是default
在接口中,方法默认是abstract
可以没有抽象方法
抽象类与类之间
子类继承都使用extends
原则
不能被new实例化,哪怕里面没有抽象方法 你说的new的同时重写方法那种不叫直接实例化
不能被final修饰,必须有子类,必须被继承
子类必须重写抽象类的所有抽象方法
抽象类的对象实例化可以通过:new 子类对象,向上转型为抽象类
抽象类 f =new 子类()
接口
明明有了抽象类,为什么还要引出接口的概念? java采用单继承避免了多重继承中容易出现的“菱形继承”,但这样单继承就缺乏多继承所拥有的优势。所以要引入接口的概念 类可以多实现接口,单继承类。因为接口不实现方法,只负责声明方法。这样就避免了方法多义性 接口作为系 统与外界交互的窗口,接口体现的是一种规范。对于接口的实现者而言,接口规定了实现者必须向外提供哪些服务 (以方法的形式来提供);对于接口的调用者而言,接口规定了调用者可以调用哪些服务,以及如何调用这些服务(就 是如何来调用方法)。当在一个程序中使用接口时,接口是多个模块间的耦合标准;当在多个应用程序之间使用接口时, 接口是多个程序之间的通信标准
接口与类之间
接口不是类,抽象类是类
一个类“实现”接口,用implements
接口与接口之间
一个接口继承另一个接口,用extends
一个接口可以继承多个接口
内部
方法
jdk1.8以前
只有抽象方法 不写abstract 默认为抽象方法
抽象方法不能被定义
jdk1.8以后
可以用default,static定义普通方法
普通方法要被定义
不写default默认为抽象方法
属性
所有属性都是 static final类型
权限
子类和外包,哪一个范围更大?子类难道不是和父类一起放在同一个包内的吗?
解释:一般父类和子类不是放在同一个包里面的 一个包里面放有相类似功能的类/接口,而不是装有一整个从接口到实现类
所以,包 类的封装大小范围: 一个类中<一个包中<该类的子类(子类位于外部包中)< 外部的包
private<什么都不写<protected<public
继承
extends 扩展
this关键字
应用
this()
从本类内调用其他的构造方法
作用
可用于多个有参构造函数之间的嵌套调用
但不能出现有参构造函数之间的递归
可提升构造方法中执行代码的可重用性
this.() /this.属性
先从本类查找,再去父类查找
this
当前对象
如public boolean equals(Object obj){ retrun this==obj}
原理
作用
用于搭配构造方法将实参的值赋给对象指针指向的成员属性
含义
描述当前对象
如果定义了一个子类对象,子类对象调用了父类公共方法,父类公共方法中有this.属性,这里的this指的是该“当前对象”而不是父类对象
super关键字
super()
从子类的构造方法调用其父类的构造方法
直接在父类查找
原理
含义
父类对象
作用
用于搭配构造方法将实参的值赋给对象指针指向的成员属性
限制
不能调用父类的private属性,方法
若父类有私有属性,公共方法,应编写父类的公共方法返回私有属性的值(如getNum) 如果有多个私有属性需要返回,可以return字符串一次性返回所有私有属性
应用
super.()/super.属性
若想调用父类方法和属性,不能省略super,否则会调用this.()
super()
只能放在构造方法里,而且是首行
作用
可用于多个有参构造函数之间的嵌套调用
但不能出现有参构造函数之间的递归
可提升构造方法中执行代码的可重用性
super
父类对象
多态
对象多态性
向上转型
向下转型
方法多态性
重载
一个方法名称有不同的实现
覆写
父类的一个方法,不同的子类可以有不同的实现
面向对象类的继承和多态
面向过程 只关心我要怎么实现功能,把函数一个个封装好,不论是谁调用他都无所谓 我在乎做这件事情本身 面向对象 强调调用函数的人是谁,不光要实现功能,还要把一个个功能封装到一个个执行者里 我在乎谁做这件事 面向接口 接口体现的是一种规范和实现分离的设计哲学,充分利用接口可以极好地降低程序各模块之间的耦 合,从而提高系统的可扩展性和可维护性。 基于这种原则,很多软件架构设计理论都倡导“面向接口”编程,而不是 面向实现类编程,希望通过面向接口编程来降低程序的耦合。
类内部
可以对本类方法进行重载
构造方法重载
作用
在new一个对象时,想给对象赋给成员属性值,可直接编写多个重载的构造方法去接收实参
方法名相同,参数类型和个数不同
返回值类型也可以修改
普通方法重载
方法名相同,参数类型和个数不同
类与类之间
UML类图的使用
继承
类:父类与子类之间的转型
关键字
对象A instanceof 对象B
判断对象所属属性
instanceof()方法确保对象向下转型的安全性
对象向上转型
Person p=new superman()
向上转型的过程本质上也是一个强制类型转换 比如
int a='s'
虽然发生转型,但对象p依然包含着superman的属性,只是装箱了而已
Object类
所有类的父类,祖宗类 所有类的super() 都调用了Object的构造方法
Java是单继承的,继承了Object类不就不能够继承其他类了吗? 确实是单继承,但Object类是祖宗类,和你要继承的其他类不是平行关系。
拥有的方法
toString()
在对象输出时默认被调用
对象向下转型
=对象向上转型+对象强制类型转换
superman p1=(superman)p
编译类型和运行时类型
Shape s=new Rectangle()
编译类型:Shape 声明的类型 在栈中的类型
编译类型决定 s.xxx() 可以直接调用的方法 不会被编译器报错的方法
运行时类型: Rectangle: 表示在堆中构建的地址的类型
运行时类型决定 能够调用到哪些方法 可能会被编译器报错的方法,但在运行时是可以调用的方法
需要将对象强制转为编译类型 才能再调用运行时类型的方法
Rectangle r =(Rectangle)s;
在实例化子类对象时,子类无参构造方法会默认执行super()父类构造方法,所以编译器会自动实例化一个子类对象和一个父类对象,其父类对象位于子类对象内部,它被包裹起来
子类的权限不能低于父类的权限
里氏替换原则
为了向上转型和Java程序可维护性
方法:子类对父类进行覆写
方法覆写
只能覆写父类的普通方法
对 非private 的方法可以覆写
子类不能比父类有更严格权限
方法名 参数类型和个数 全部和父类的被重写方法相同
包括返回值类型和入口参数类型
属性覆写
对 非private 的属性可以覆写
子类不能比父类有更严格权限
依赖
是一种使用的关系, 即一个类的实现需要另一个类的协助, 所以要尽量不使用双向的互相依赖 如丈夫和情人 利用关系
依赖另一个类:本类对另一个累的局部变量、方法的参数或者对静态方法的调用
带箭头的虚线,指向被使用者 尽量不能出现双向依赖
关联
是一种拥有的关系, 它使一个类知道另一个类的属性和方法;如:老师与学生,丈夫与妻子 你中有我
带普通箭头的实心线,指向被拥有者 。双向的关联可以有两个箭头或者没有箭头,单向的关联有一个箭头。
关联另一个类:把另一个类作为本类的成员变量
实现
聚合
组合
面向接口编程
接口通信
Activity与Fragment通信,先定义一个接口,其中有两个抽象方法send和receive
Fragment类定义提供给Activity用于调用的回调方法(用于传入Activity实例化的对象)和接收Activity实例化对象的接口对象
Activity类调用Fragment类定义的回调方法,并通过匿名对象的方式重写两个抽象方法
最后就实现了,对Fragment进行操作,传入send方法的msg,Activity可利用msg来做一些事情在重写的方法中。
Java创建对象有哪几种方式
new创建新对象
反射
clone
序列化
一个类
这个类就像使用了.c中的多个方法的一个函数体(比如程序初始化汇总函数=dht11初始化+led初始化)
一段程序
接口
在这个被调用的过程中,接口就像是.h,负责声明方法,而所有子类就像.c 负责定义方法,所以在调用时,只需要看接口有哪些方法就可以了
接口
继承接口
继承接口
抽象类
抽象类
类
类
类
类
类
类
同样实现所有抽象方法,但和其他的子类有不同的内容
抽象类
类
实现最顶层的接口的所有抽象方法,不添加新的方法
JAVA SE
反射机制
反射
基本概念
什么是反射
一般的代码执行过程: hello.java->hello.class->fun.method() 源文件编译 字节码文件连接 运行 反射的过程: 运行->字节码文件->构造方法、属性、普通方法等类的信息 整一个代码在运用反射的过程:正反结合的过程 源文件编译->字节码文件连接->运行->字节码文件->反编译类信息 而反射在编写代码期间是看不到是否有报错的,只有当代码开始运行起来时,会动态的去进行,所以被称为动态特性
反射能做什么
一开始不知道我要初始化的类对象是什么 自然就不能简单的使用new来创建对象了 反射就是在运行时才知道要操作的类是什么,并在运行时获取类的完整构造,并调用对应的方法(刚开始先跑一遍看看有哪些方法、属性 下一次再invoke)
分析一个隐藏类的属性和方法
反射能读到你任何想读的代码,在源码里找不到的隐藏类也能读的到
类文件
什么是Class对象
你写的每一个类,都是Class类的实例化对象,只是平时写的时候,没有显式的去new Class对象 当你进行编译以后,你所写的所有的类的方法,属性等信息将会被类加载器存放在Class类的加载的后面, 类变量比如:静态属性 静态方法 构造方法 存放在方法区中 普通属性,普通方法,存放在堆中
用法
先获取Class对象
根据对象获取
实例化该类对象
Class c1=对象.getClass()
根据类名获取
Class c1=类名.class
根据类名字符串获取
CLass c1=Class.forName("包名.类名")
调用类加载器
再使用该Class对象进行实际反射操作
获取Field /Method/Constructor
获取属性和方法 构造器的方式大同小异 都是先尝试缓存获取,如果找不到,就调用native方法获取
1.从reflectionData() 里面取属性 是一种缓存获取 reflectionData是弱引用
2.直接调用getDeclaredFileds() 是一个本地方法 应该是从JVM内获取的
通过反射获取到的属性有没有对象属性值?
有,因为field对象来自jvm,jvm保存着对象的属性值
使用Field /Method/Constructor
使用构造方法
Constructor
使用成员属性
Field
getField 返回这个类的公共属性 getDeclaredField 返回这个类的所有属性包括私有属性
调用成员方法
Method
getDeclaredMethods 只返回这个类的方法 getMethods 返回这个类以及它的父类的所有方法 getMethod(“”) 返回指定的方法 通过反射调用私有方法前,要设置可访问 Method m1=cl1.getDeclaredMethod("方法名",形参.类型class) method.setAccess(true) Object o1=cl1.newInstance() method.invoke(o1,实参)
总体上是通过反射工厂+代理模式去invoke该方法的
反射API
Class类
Field类
Method类
Constructor类
应用
优点:运行时动态获取实例,与动态编译结合
缺点:反射性能低,需要解析字节码
可通过setAccessible(true)关闭JDK的安全检查提升反射速度
ReflectASM工具类,通过字节码生成的方式加快反射速度
原理
类加载
当程序要使用某个类是,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类 进行初始化。 如果不出意外情况,JVM将会连续完成这三个步骤。这三个步骤统称为[类加载]或者[类初始化] 类的生命周期是 类的加载->类的连接->类的初始化->类使用->类卸载 即前3步+使用和卸载
类的加载
类的连接
验证
准备
解析
类的初始化
类加载器
应用程序加载器
AppClassLoader 你自己写的类的类加载器就是他
扩展类加载器
ExtCLassLoader 或者PlatformClassLoader 应用程序加载器的父类加载器
引导加载器
Bootstrap 扩展类加载器的父类加载器 无法打印 因为是使用C++编写的类加载器,是在JVM启动后进行初始化操作
类加载机制
双亲委派
为什么要这样设计?
泛型
概念
什么是泛型
思考HashMap的底层实现,你传入Integer和String,Map类的内部就会使用这两个指定的类型进行计算 Map m1=new HashMap(); public class Map
就是参数化的类型 T是一种数据类型,可假设看做String 记住他一定是一个类 不是int
纠正一个认知:List<Integer> l1 你会认为List是一个数组 然后比如MutabelLiveData<Data> m1也是一个数组,还会有add方法,其实你应该忽略掉<Data> LiveData就是一个类而已,只是这个类是泛型类,导入一个指定类型Data罢了
为什么要使用泛型?
如果接收参数类型都使用Object,就会造成接受范围过大,对装箱无影响,但拆箱时要强制向下转型,就会导致强制转换时报错
为了创建“容器类”
原理
泛型
语法糖,本质是编译器为了提供代码可读性提供的手段,虚拟机不存在泛型
泛型只存在于编译阶段
不存在于运行阶段
泛型擦除
编译器会擦除类后面的尖括号,将类型定义为Object,如果使用了T extends String 那T就会被擦除成 String
应用
怎么用?
泛型标记在一个类或一个方法中出现一次就可以了。作用是一个声明
泛型类
泛型上限
extends
泛型下限
super
继承
可以继承泛型接口
定义
class 类名<T>
类名<T> 在类名后面紧跟小括号T是语法,等同于该泛型类的类名为“类名<T> ”
泛型方法
定义
一定要有<T>做标记 输出参数和输入参数可以没有T
public <T> 输出类型 方法名(args)
使用
不论是泛型类还是普通类都可以定义泛型方法
那为什么需要打上泛型标记<T>呢?
如果该方法的输入输出参数想要使用泛型,而他所处的类或接口没有泛型标记,那这个方法就要有泛型标记
属性
类名<类名> 属性名
List<Integer> l1
ObservableField <Integer> age
类名 属性名
T age
泛型接口
限定类型变量
有时需要对类型变量加以约束,如何确保传入的两个变量一定会有cpmpareTo()方法呢?就要讲T限制为实现了接口Comparable的类
public <T,K extends Cpmparable>
对象
类名 <动态数据类型> 对象名=new 类名<动态数据类型> ()
可变参数
int...a
本质是数组 int[] a
集合
各个类型集合
优缺点
使用场景
List
ArrayList
object数组
LinkedList
双向链表
Vector
object数组
线程安全
Set
Hashset
数组+链表/红黑树
LinkedHashSet
数组+链表/红黑树
Treeset
红黑树
Map
HashMap
数组+链表/红黑树
遍历方式:Iterator
hashCode() 和equals()区别
TreeMap
红黑树
HashTable
数组+链表
线程安全
遍历方式:Enumeration
ConcurrentHashMap
数组+链表/红黑树
线程安全
JAVA API
包装类
对基本数据类型进行封装成类,实现实例化赋值
装箱和拆箱
jdk1.5以前
Integer obj=new Integer(10);
装箱
用构造方法set
向上转型
int num=obj.intValue();
拆箱
用普通方法get
向下转型
jdk1.5以后
Integer obj=10;
自动装箱,不再考虑构造方法
int num=obj
自动拆箱,等价于调用intValue方法
数据类型转换
一般java程序中所有输入的内容都用string类型描述,所以需要将string转为其他类型
String转int
Integer.parseInt(字符串)
String转boolean
Boolean.parseBoolean(字符串)
其他类型转String
String.valueOf(其他类型变量)
Integer是int的包装类,使得int基本数据类型可以通过实例化对象赋值,是补充关系不是替代关系
Integer和int的比较
Integer和Integer进行比较,由于都是对象的引用,==号始终比较两个地址,所以为false
Integer和int比较,java会自动拆箱Integer为int,==时为true
Integer i=new Integer(100)
非new生成的Integer和new Integer()生成的比较 ==
Integer i=new Integer(100)
new Integer()生成的变量指向堆中新建的对象
Integer j=100
非new的话,两种情况
1.变量值在-128~127之间,指向的是常量池中对象
java对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了
2.变量值在其之外,指向堆中新建对象
两个都是非new生成的Integer
1.在-128 ~127之间 地址相同 为true
Integer i=100
Integer j=100
2.不在其间,地址不同,为false
Integer i=128
Integer j=128
枚举
enum
被简化的类 它是用来替代静态常量的 静态常量太多就会很难受,所以要用枚举。枚举内部的都是静态常量
注解
@
注解是什么
注解和注释的区别
注释会在编译期间忽略掉,不会编译进字节码 .class内 而注解是会的,会编译进字节码中
注解和反射的关系
利用反射获取Annotation类的信息, 反射是对注解进行操作的,不是两两协同合作的
注解本质是一个继承了Annotation的接口,具体实现类是Java运行时生成的动态代理类,通过代理对象调用自定义注解(接口)的方法 该方法会从memberValue这个Map中索引出对应的值,而memberValue的来源是Java常量池
概念
分类
标准注解
过期
忽略警告
元注解
用于创建新注解
原理
应用
注解有什么用
在程序编译、运行期间保留注解,根据你的需要可以修改程序的执行状态
比如在代理模式中,你有时候想要执行Cloud 有时候想执行Net,就用一个注解,令其=Net,如果需要改动就直接改注解,不用动代码
IO操作
阻塞IO
输入输出
文件外部crud
File类
批量文件操作
RandomAccessFile
文件内部数据操作
字节流
适用于网络传输 底层数据交换
inputstream
outputstream
字符流
适用于文字处理 文本处理 中文数据处理 更高级
writer
reader
内存
字节内存操作流
字符内存操作流
线程
管道输出流
管道输入流
字符字节转换流
序列化
把对象变为二进制数据流
常用的键盘输入输出
BufferedReader缓冲输入流
Scanner输入流
PrintWriter打印流
System类
err常量对象
out静态常量对象
属于PrintStream类
拥有print() println()方法
in静态常量对象
属于InputStream类
应用
完整的IO读请求操作
1.查看数据是否就绪
如果未就绪,就继续等待
例子:socket的read方法会一直阻塞
2.进行数据拷贝(内核将数据拷贝到用户线程)
非阻塞
问题
NIO 同步非阻塞多路复用模型?
应用
完整的IO读请求操作
1.查看数据是否就绪
如果未就绪,就直接返回一个标志信息,不会等待
2.进行数据拷贝(内核将数据拷贝到用户线程)
BIO 同步并阻塞
BIO:线程发起IO请求,不管内核是否准备好IO操作,从发起请求起,线程一直阻塞,直到操作完成。
不论是否准备好
例子:烧水,你要一直等着水烧开
场景
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
NIO 同步并非阻塞
NIO:线程发起IO请求,立即返回;内核在做好IO操作的准备之后,通过调用注册的回调函数通知线程做IO操作,线程开始阻塞,直到操作完成。
准备好后才通知线程
例子:烧水,你在烧水期间做别的事,但时不时要看看水烧没烧开
场景
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO 异步并非阻塞
AIO:线程发起IO请求,立即返回;内存做好IO操作的准备之后,做IO操作,直到操作完成或者失败,通过调用注册的回调函数通知线程做IO操作完成或者失败。
例子:烧水,水烧开会有通知,不需要看水烧没烧开
场景
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
IO中用到的设计模式
适配器模式
装饰器模式
异常的捕获和处理
自定义异常类
extends Exception
重写方法
try
catch
finally
如果在catch中return了,finally还会执行吗?
会,先记录下返回值待finally处理完再返回,如果finally中又return一个新的,那就会返回finally
1.finally和catch都return不同的值,会返回finally的
返回路径由catch改成finally
2.catch return一个变量a,a=30 finally修改a=40, 最终还是return 30 因为编译期间 return把a 变成 常量30了 return 30
返回路径依然是catch
printStack
JVM如何处理异常?
一个方法发生异常,会创建一个异常对象,转交给JVM,异常对象包含异常名称、异常描述、异常发生时应用程序的状态
运行时创建对象,是动态代理?
创建异常对象并交给JVM就叫抛出异常
深入理解JVM
1.Java内存区域 JVM内存结构
概念
java内存区域
jvm运行时将数据分区存储,不同的区域放不同地方
class文件
类加载子系统
运行时数据区(java内存区域)
堆 线程共享 属于进程
引用数据类型在栈中分配地址空间,在堆中分配对象的类变量
方法内定义局部对象,当方法调用完成后,堆空间中的对象等待GC回收,其他与方法相关的参数,随着栈帧一同立刻被回收
方法区 线程共享 属于进程
概念
存放类的信息
常量池
final量
全局字符串常量池
class字节码文件常量池
运行时常量池
静态区
static量
问题:static instance=new Singleton(); 这句话,这个变量存放在哪里?
栈 线程私有
虚拟机栈
概念
每个方法被执行时就创建一个栈帧,存储局部变量表,操作数栈、动态链接、方法出口等信息。直到执行完,栈帧在虚拟机栈中从入栈到出栈的过程 基础数据类型也在jvm栈中分配
方法的形式参数在栈空间分配,方法调用完成后从栈空间回收
栈帧
局部变量区 基本数据类型 +引用数据类型的引用
操作数栈
本地方法栈
jni
本地方法栈和虚拟机栈是一样的,都是每次调用一个方法就创建一个栈帧,区别是虚拟机栈执行Java方法,本地方法栈执行Native方法
程序计数器
执行引擎
字节码解释器
JIT编译器
垃圾回收器
本地方法接口
jni
本地方法库
C/C++库
1.Java内存模型JMM
java内存模型JMM
定义程序中各个变量的访问规则
在虚拟机中将变量存储到内存和从内存中取出变量的底层细节
这只是个虚拟的抽象概念,不是真实的物理内存划分,真实的物理内存划分叫Java内存区域
JMM是JVM的内存使用规范
JMM中的内存划分
线程
本地内存
从主内存中拷贝过来的 共享变量的副本
主内存
共享变量
主线程
主内存
子线程
工作内存
2.内存溢出异常
3.垃圾收集器
gc的过程
1.判断哪些对象需要回收
2.根据对象特性使用相应的回收算法
3.选用合适的垃圾回收器
jvm说的gc需要完成的3件事情
哪些内存需要回收?
什么时候回收?
如何回收?
我认为的过程
1.判断哪些对象需要回收
GC Roots不可达的对象
GC Roots是一组对象
虚拟机栈中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI引用的对象
2.什么时候回收?
内存将满?没有引用?
3.如何回收?
将对象关联引用队列,标记2次后再回收引用队列内的所有对象
Java四种引用
引用队列
概念
被加入到引用队列的对象将会被回收
软、弱、虚引用可以和一个引用队列进行关联,如果这个软引用的对象被垃圾回收,就会将这个软引用加入到关联的队列中去。
疑问
引用队列会不会存在 满的情况??
分类
强引用
概念
可以直接访问目标对象
可能会导致内存泄漏
何时被回收?
任何时候都不会回收
任何时候都不会被垃圾回收器回收,如果内存不足,宁愿抛出OutOfMemoryError 置为null呢?
应用
我们平常大部分使用的场景都是使用了强引用,比如new创建对象,反射获得一个对象等。
软引用
软引用是“只有当系统内存不足时,软引用对象才会被回收,加入到与之关联的引用队列”
概念
何时被回收?
内存快要满
只有在内存将满的时候才会被垃圾回收器回收,如果还有可用内存,垃圾回收器不会回收。
应用
可用于高速缓存????
弱引用
弱引用是“在垃圾回收器工作时,不论内存是否不足,弱引用对象随时都会被回收,加入到与之关联的引用队列”
概念
何时被回收?
垃圾回收器运行就会回收
弱引用,生命周期更短,只要垃圾回收器运行,就肯定会被回收,不管还有没有可用内存。
但由于垃圾回收器的线程优先级很低,所以加入到引用队列后不一定会被很快回收
应用
弱引用用于生命周期更短的,对内存更敏感的场景中
WeakHashMap
key只有弱引用时,会自动清理键值
ThreadLocal
为什么它要用弱引用???
虚引用
虚引用 垃圾回收器可在任意时候对其回收
概念
何时被回收?
任何时候都有可能被回收
虚引用,虚引用等于没有引用,不能决定对象的生命周期。任何时候都有可能被垃圾回收。
虚引用必须和引用队列联合使用
啥意思???????垃圾回收器不工作,也能回收虚引用对象吗???
应用
判断一个对象是否被垃圾回收了,什么时候引用队列有新的引用入队了,就说明他被回收了。
用于跟踪对象被垃圾回收期回收的活动
没有引用
因为某些操作,使得强引用等引用方式中的对象,造成了没引用的情况,就叫没有引用 比如单链表操作 删除结点等等
即使在可达性分析算法中不可达的对象,也不一定意味着就会被回收(回收即死亡) 至少要两次标记
1.可达性分析后发现没有和GC Roots相连接的引用链,就会第一次标记并进行一次筛选
筛选条件:此对象是否有必要执行finalize()方法,
2.GC会对F-Queue中的对象进行第二次标记
如果对象能在调用finalize()方法时成功和引用链上任何一个对象建立关联,就能在第二次标记时逃脱“即将回收”的集合
gc常用算法和回收器
扫描算法 判断哪些对象需要回收
引用计数法
引用计数 按照对象的使用次数进行从少到多的清理
可达性分析算法
跟踪 可达性分析算法 根据对象是否能被引用进行清理jdk是这么干的
清除算法 根据对象特性使用相应的回收算法
目前的jvm是分代收集+标记整理
分代收集
现在jvm用的是分代收集 有不同年代的回收器 分别用的是各自年代的标记整理去做
标记清除
会有内存碎片
复制
耗时
标记整理
垃圾回收器
老年代回收器
有serialOld CMS 还有jdk1.7在用的g1收集器标记整理算法
新生代的垃圾回收器
serial 串行回收器 复制算法 用在单线程
parnew并行垃圾回收 多线程
parallelgc 多线程 复制算法
4.内存分配策略
5.虚拟机性能监控与故障处理工具
jvm命令
jvm查gc次数
6.调优分析
概念
JVM调优,调的是什么? 每一次Full GC都会使JVM停止运行–>使Full GC不执行,使Minor GC尽可能少地执行
原理
应用
减少GC开销的措施
不要显式调用System.gc()
尽量减少临时对象的使用
对象不用时最好显式置为Null
尽量使用StringBuffer,而不用String来累加字符串
能用基本类型如Int,Long,就不用Integer,Long对象
尽量少用静态对象变量
方法区不会被GC回收,他们会一直占用内存
分散对象创建或删除的时间
第三章、程序编译与代码优化
Java编译和执行的过程
Java代码编译和执行的过程
编写Java代码(.java) 编译成字节码(.class) 字节码被类加载器装入内存,进入虚拟机后被字节码解释器(执行引擎)解释执行
Java源码编译机制
类加载机制
类执行机制
Java源码编译机制
分析和输入到符号表
注解处理
语义分析和生成class文件
jvm 对多态的实现??比如父类声明怎么找到子类对象的?
第二章、虚拟机执行子系统
类加载机制
类加载
当程序要使用某个类是,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类 进行初始化。 如果不出意外情况,JVM将会连续完成这三个步骤。这三个步骤统称为[类加载]或者[类初始化] 类的生命周期是 类的加载->类的连接->类的初始化->类使用->类卸载 即前3步+使用和卸载 双亲委派机制 先向上检查类是否已被加载。再向下尝试加载 加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
类的加载
ClassLoader及其子类
类的连接
验证
准备
解析
类的初始化
类加载器
应用程序加载器
AppClassLoader 你自己写的类的类加载器就是他
平台类加载器(扩展类加载器)
ExtCLassLoader 或者PlatformClassLoader 应用程序加载器的父类加载器
系统类加载器
Bootstrap 扩展类加载器的父类加载器 无法打印 因为是使用C++编写的类加载器,是在JVM启动后进行初始化操作
利用继承创建类的步骤
创建父类
创建子类
初始化所有父类的类(静态)变量
初始化所有子类的类(静态)变量
执行父类的构造方法
执行子类的构造方法
类执行机制
new一个对象的过程???
其他问题
jvm的应用
应用
深拷贝和浅拷贝
深拷贝(对象拷贝)
原理
深拷贝会复制栈中引用和堆中对象,创造一个一模一样的对象 深拷贝把要复制的对象所引用的对象都复制了一遍。 拷贝的地址和原来的是不同的 说明指向不同的对象
应用
第一种:让每个引用类型属性内部都重写clone方法,也就是将每个引用类型都拆分为基本类型,分别进行浅拷贝
第二种:利用序列化,利用对象的序列化产生克隆对象,然后反序列化获取该克隆对象 实现Serializable接口
浅拷贝(引用拷贝)
原理
引用数据类型:浅拷贝只复制栈中引用,仍然指向堆中同一个对象 输出的地址值和原对象在栈中的地址值也是一致的
基本数据类型:浅拷贝就拷贝基本类型的值
应用
直接针对你想浅拷贝的类,实现Cloneable接口,调用clone()方法就行
创建对象的4种方式
new关键字
反射
Class类的newInstance()
Person p2 = (Person) Class.forName("com.ys.test.Person").newInstance();
Constructor类的newInstance()
Person p3 = (Person) Person.class.getConstructors()[0].newInstance();
Object类的Clone()
Person p4 = (Person) p3.clone();
深拷贝
调用对象的 clone 方法,必须要让类实现 Cloneable 接口,并且覆写 clone 方法
反序列化
JVM和Davik VM区别
概念
Java虚拟机运行的是Java字节码
Dalvik虚拟机运行的是Dalvik字节码
原理
java虚拟机与Dalvik虚拟机架构不同。
java虚拟机基于栈架构。程序在运行时虚拟机需要频繁的从栈上读取或写入数据。这过程需要更多的指令分派与内存访问次数,会耗费不少CPU时间,对于像手机设备资源有限的设备来说,这是相当大的一笔开销。
Dalvik虚拟机基于寄存器架构,数据的访问通过寄存器间直接传递,这样的访问方式比基于栈方式快的多.
Dalvik可执行文件体积更小。SDK中有一个叫dx的工具负责将java字节码转换为Dalvik字节码
弱引用和虚引用 何时被回收有什么区别??
脑海里有一个动图,描绘一个程序运行时,出现的变量参数等如何被定义,销毁,在哪里?
操作系统
进程
概念
进程是“操作系统”资源分配的基本单位
进程表
为了维护进程间的切换
进程管理
存储管理
文件管理
运行状态
运行
就绪
阻塞
通信方式
进程通信的本质 是内存拷贝
不同进程间的通信本质:进程之间可以看到一份公共资源;而提供这份资源的形式或者提供者不同,造成了通信方式不同,而 pipe就是提供这份公共资源的形式的一种。
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,进程B再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。可以理解为两个Java程序之间的通信
由于各个进程之间是资源独有的,有进程隔离,所以进程不能直接访问另一个进程的用户空间
内核空间是所有进程共有的,所有进程既拥有用户空间也有内核空间
两次拷贝
1.进程A发送数据
2.通过系统调用copy_from_user()将数据从用户空间copy到内核空间的内核缓冲区
3.通过系统调用copy_to_user()拷贝回进程B的用户空间
4.进程B接收数据
一次拷贝
进程A直接拷贝到进程B的内存空间中
零次拷贝
通过虚拟映射机制 让进程A和进程B共用同一块内存
各个通信方式之间的区别是什么?形象??
具体到每个通信方式的Java代码实现???
每个通信方式的应用场景???
管道
概念
管道位于内核层,两个进程都位于用户层,
父进程创建管道
形成一个父进程有两个端口分别连接管道两段
父进程fork出子进程
父进程关闭一个端口,子进程关闭另一个端口
形成父进程读/写,子进程写/读的单向管道
数据单位
无格式的字节流
优缺点
缺点是通信效率低 不适合进程间频繁的交换数据 向管道内写数据会卡一会直到写入完毕
2次拷贝
原理
匿名管道
只允许父子进程通信 子进程必须是被父进程fork出来的
生命周期
随进程的创建而建立,进程的结束而销毁
有名管道
可以和不相关的进程通信,因为命名管道提前创建mkfifo了一个类型为管道的设备文件,只要使用它就能通信
生命周期
有名管道的创建是单独的,所以生命周期是随内核的结束而销毁
管道通信的原理
管道就是内核里面的一串缓存。从管道一端写数据,实际上缓存在内核,读数据就是从内核态读出
应用
匿名管道
ps xxx | grep mysql
将前一个命令 ps xxx 的输出 作为后一个命令 grep mysql 的输入
有名管道
mkfifo xxx
创建一个有名管道,命名为xxx
echo "hello" > xxx
向xxx有名管道写入数据
执行命令后会卡一会 直到写入完毕
cat < xxx
从xxx有名管道读出数据
管道通信
fork
先fork出一个
不同的管道通信方式的选择依据是????
消息队列
概念
优缺点
优点:比管道效率高,不会写数据时卡一会
缺点:通信不及时。消息有大小限制,不能超过消息体的最大容量
2次拷贝
原理
消息队列是保存在内核中的 “消息链表”
数据格式:
消息体是自定义的数据类型,收发双方提前约定,每个消息体都是固定大小的存储块
进程从消息队列中读取消息体后,内核就会把消息体删除
应用
消息队列的创建和使用?????????????????????????????????????
共享内存
最快的进程通信方式
不安全
概念
零次拷贝
优缺点
优点是快 比消息队列,管道都快
缺点是多线程场景下容易发生并发问题 两个进程同时写入,读取会出错
本质是共享资源,被多线程相互“竞争”
原理
共享内存就是 两个进程的不同的虚拟内存空间地址,同时映射到一块相同的物理内存空间中
Socket
可用于不同机器之间的进程通信 基于端口号 因为linux一切皆文件 所以端口也是文件 所以还是基于文件的读写 基于C/S架构
概念
原理
安全
两次拷贝
应用
可实现 TCP UDP 本地进程三种通信方式
信号量
概念
信号量表示资源的数量
优缺点
优点 解决了共享内存的并发问题
用处
信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据传递消息的
原理
信号量就是 整型的计数器。用于实现进程间的互斥和同步,不是拿来缓存进程间通信的数据的,
应用
P操作
在进入共享资源之前使用
操作后会把信号量-1
如果-1后 信号量<0 表示资源已经被占用,执行P操作的当前进程需要阻塞等待
如果-1后 信号量>=0 表示资源还可使用。当前进程可继续执行
V操作
在退出资源后使用
操作后信号量+1
如果+1后 信号量<=0 表示有进程正在被P操作所阻塞,执行V操作的当前进程就会对那个进程唤醒
如果+1后 信号量>0 表示没有被阻塞的进程
PV操作必须成对出现
执行一对PV操作能保证信号量始终为初始值 例如 先由1-1==0 再由0+1==1
信号量的初始值
初始值为1表示互斥信号量。
保证内存在任一时刻只有一个进程在访问共享资源
进程A做P操作后进入共享内存,出来后做V操作,进程B再P V
初始值为0表示同步信号量
保证线程之间按照一定顺序执行
例如 进程A生成数据后做V操作,0+1==1,唤醒进程B做P操作,1-1==0,再做读取数据
信号
概念
优缺点
优点是无需知道进程的状态 就可以对该进程进行操作
缺点是一般用于异常情况。不是用于传输数据的,而且Linux系统的信号的种类有限
应用场景
对于异常情况下的工作模式,需要用“信号”的方式来通知进程
可以在任何时候发给一个进程,无需知道该进程的状态
用处
一个进程对另一个进程进行某些操作
原理
信号事件的主要来源是:硬件,ctrl+c 软件 ,kill命令
用于通知某进程做特殊的处理 如终止该进程 它不是拿来缓存进程间通信的数据的
信号是IPC中唯一的“异步通信机制”
应用
Linux命令 kill -l查看所有的信号
信号都以SIG开头
运行在shell终端时,可通过键盘输入某些组合键,给进程发送信号
同步方式
在多道程序环境下,进程是并发执行的,不同进程之间存在着不同的相互制约关系。为了协调进程之间的相互制约关系,引入了进程同步的概念。
进程之间存在同步与互斥
互斥是指当一个进程进入临界区使用临界资源的时候,另外一个进程必须等待,当占用临界资源的进程退出临界区后,另外一个进程才允许去访问此临界资源
同步是指为了完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调它们的工作次序而等待,传递信息所产生了制约关系
两个进程之间能通信 那这两个进程一定是同步的 也就是传递,制约,等待
临界区
临界区就是一个线程去访问公共资源的代码段
例如一个全局变量,被多个线程所访问,其中在他们各自的run代码内有对该全局变量的使用,那部分代码就是临界区
临界资源就是公共资源,能被多个线程访问,但同一时间只能被一个线程访问,互斥的
synchronized(全局变量)
临界区只能在同一进程中使用
应用
如何解决临界区冲突
1.一次仅允许一个进程进入
2.进入临界区的进程要在有限时间内退出
3.如果进程不能进入临界区,就让出cpu
互斥量
和临界区类似,
互斥量是进程与进程之间使用,临界区是线程与线程之间使用
互斥量可命名,可跨越进程使用,但所需资源更多,而临界区能减少资源占用并提高速度
该不会就是安卓的系统属性吧???????
信号量
概念
限制同一个公共资源被多少个线程所访问的数量
可跨越进程使用
用处
信号量本就是用于进程间同步和互斥的,而不是进程间传递数据的
应用
怎么保证两个进程用的是同一个信号量??
事件
一个线程完成任务后,notify唤醒另一个线程
可跨越进程使用
6+4
进程切换
1.切换页表以使用新的映射
同时cpu内的LRUcache也作废了,要重新更换
2.切换内核栈和硬件上下文
进程的分类
CPU密集型进程
有较长的CPU集中时长和较小的IO等待
区别在于CPU的时间占用的多少
IO密集型进程
该进程的IO发生频率快
进程调度
概念
调度程序
操作系统最底层的程序是调度程序,负责着上层所有程序的调度
调度算法
调度程序执行的算法叫调度算法
调度分类
批处理
特点
吞吐量:每小时最大作业数
周转时间:从提交到终止间的最小时间
CPU利用率:保持CPU时钟忙碌
尽量减少切换的开销
算法
先来先服务
减少线程切换的开销时间
最短作业优先
最短时间优先:作业的总时间进行排序 最短剩余时间优先:作业还剩余多少时间就完成的排序
每个作业有已知的周转时间
在所有进程都可以运行的情况下,最短作业优先算法是最优的 因为“平均每个作业的周转时长最小”
也就是等待前面作业的时间+执行该作业的时间的平均时长
最短剩余时间优先
是最短作业优先的抢占版本
交互式
特点
响应时间: 快速响应请求
尽量让每个进程都能得到时间片
均衡性:满足用户的期望
算法
轮询算法
优先级调度
最短作业优先
和批处理的一样
彩票调度
随机抽奖,获奖者可以获得一部分cpu时间的奖励
公平共享调度
实时
特点
满足截止时间: 避免数据丢失
可预测性: 多媒体系统中避免品质降低
分类
硬实时
不可容忍错失ddl
软实时
可以容忍
调度机制
位于内核
调度策略
由用户进程决定
2.线程
线程
线程状态有哪些
新建状态new
就绪状态runnable
运行状态running
阻塞状态blocked
死亡状态dead
这是操作系统的线程
通信方式
管道流
消息传递
共享内存
同步方式(解决互斥问题)
场景
生产者-消费者
生产者生产数据set,消费者取数据get
实现方式
操作缓冲区是临界代码,需要互斥
生产者和消费者需要同步,生产数据后才能消费数据,以此类推
哲学家就餐
分类
锁
synchronized
信号量
原子操作CAS
线程切换
切换内核栈和硬件上下文
上下文切换: CPU会通过时间片分配算法来循环执行任务,当前任务执行完一个时间片后会切换到下一个任务,但切换前会保存上一个任务的状态,因为下次切换回这个任务时还要加载这个任务的状态继续执行,从任务保存到在加载的过程就是一次上下文切换
什么是线程
线程是”cpu“处理器任务调度和执行的基本单位
应用
CPU密集型适合用线程
协程
概念
为什么要使用协程?
有10个人使用数据库,就开10个线程,那越来越多人使用,线程越开越多。
问题是系统线程占用非常多的内存空间,过多的线程上下文切换会占用大量时间
让协程运行在线程之上,协程A执行完后,主动让出让协程B继续运行,此时就不会像线程切换造成大量的时间和资源开销
而且协程的切换是在用户态完成,比线程从用户态到内核态的代价小
使用协程会遇到哪些问题?
协程运行在线程之上,当该线程因为io等待等问题发生阻塞后,在此之上的所有协程都将等待
解决方案是协程+异步IO
协程和线程的区别
线程、进程都是同步机制,协程是异步机制
线程是抢占式、协程是非抢占式,需要用户代码手动释放当前使用权才能切换到其他协程
原理
应用
kotlin
IO密集型适合用协程,减少开销
3.存储管理(内存管理)
内存池?
概念
内存(主存)的构造
存储体
控制电路
地址寄存器
数据寄存器
地址译码电路
内存分类
ROM
RAM
内存条
Cache
内存条的组成
RANK(也可以叫Chips)
内存颗粒
Bank
存储矩阵库
Cell
存储单元
编址
寻址
读写方式
内存管理机制
操作系统管内存的申请 释放
连续分配管理
块式管理
将内存分为几个固定大小的块,这样极大地浪费了空间
块>页>段
非连续分配管理
页式管理
把内存分为固定且大小相等的一页一页的
逻辑地址和物理地址
段式管理
每一段都有其实际的意义 程序段、数据段、代码段
分页和分段的共同点和区别
共同点
区别
段页式管理
把主存分为若干页 再分段
将进程按逻辑模块分段,再对段分成若干大小固定的页,整个内存分成若干个和页面大小相同的存储块,最后将进程的各个页分别装入各个内存块
交换空间
由于虚拟内存大于实际物理内存,所以内存中无法存放的页的内容将会放在硬盘上,这部分就叫交换空间
无存储器抽象
概念
早期计算机的每个程序都直接访问物理内存
缺点是如果用汇编语言编写程序,每个程序共用一个物理内存,很容易造成写地址冲突,所以同时运行两个程序是不行的
有存储器抽象-地址空间
概念
把物理内存直接暴露给进程有缺陷,应该提供个物理内存与进程之间的“接口”
空闲内存管理
监控内存使用的方式
位图bitmap
空闲列表free lists
虚拟内存
概念
分页
概念
在使用虚拟内存时,虚拟地址不会直接发送到内存总线,会使用MMU把虚拟地址映射为物理内存地址
实际内存中
页面
每个程序都有自己的地址空间,他们被划分成多个称为 “页面”的块,每个页都是连续的地址范围 这些页映射到物理内存
不全是所有的页都必须在内存中才运行程序 程序引用到不存在物理内存的逻辑地址时,操作系统将缺失的部分装入物理内存并重新执行失败的指令
页表
概念
一种特殊的数据结构,可以想象是个Map,key是逻辑地址,value是物理地址 一个进程有一个虚拟地址空间,虚拟地址空间有多个虚拟地址即key,实际物理地址位于内存中
页表是Map,提供虚拟地址映射向物理地址的结果
切换进程就要切换页表,同时cache也会作废
地址映射
cpu
转换检测缓冲区TLB(快表 也是缓存,位于页表中)
tlb是cache的其中一种 和cpu的cache都是cache,但作用不同 TLB是用于存逻辑地址向物理地址的映射的 cpu的cache是解决处理器和内存的速度差异的 存内存数据的
背景
缺页异常
存放常用的地址映射
当页表太大时,代价高昂,每次上下文切换都必须装载整个页表
能将虚拟地址直接映射到物理地址,而不必再访问页表,不然页表不存在该映射又会切换页,造成不必要的开销
虚拟内存有多大???
2的多少位次方就是虚拟内存的大小 单位是Bit
页面置换算法
最优页面
不可实现,但可以无限接近
先进先出
有可能抛弃重要的页面
第二次机会
先进先出的优化 在抛弃页面前做一次检查
最近最少使用LRU least recently used
优秀,但很难实现 需要有TLB
最近最不常用LFU least frequently used
最近未使用NRU
从编号最小的非空类中随机删除一个页面
和LRU很相似
最不经常使用NFU
和LRU很相似
时钟
实际使用
老化
工作集
工作集时钟
缓存一致性问题
背景
由于cpu执行指令速度快于读写内存的速度,所以在cpu和内存之间增加高速缓存,甚至多级缓存
当cpu要load一个数据,先从一级缓存中查找,没有就从二级缓存查找,还是没有就三级缓存
概念
在单线程中
cpu内核的缓存只被一个线程访问
在单核cpu、多线程中
进程中的多个线程会同时访问进程中的共享数据 cpu将某块内存加载到缓存后,不同线程在访问相同的物理地址时,都会映射到相同的缓存位置
在多核cpu、多线程中
每个核都至少有一个L1缓存 而不同的线程在不同的核上跑的时候,可能发生多个缓存的数据之间不一致的情况
缓存一致性问题
解决缓存一致性
1.在总线加lock锁
只能有一个cpu能在同一时间内使用这个变量
2.缓存一致性协议
MESI协议保证每个缓存中使用的共享变量的副本是一致的
当cpu写数据,如果操作的变量是共享变量也就是其他cpu也存在该变量的副本,该cpu就会发出信号
通知其他cpu将该变量的缓存行 置为无效状态,当其他cpu执行到缓存行时,发现缓存行无效,就会从主内存重新读取
4.文件系统
磁盘空间管理
分段管理
分页管理
文件
目录
文件系统的实现
文件系统的管理和优化
链接
硬链接和软链接
硬链接
和普通文件没区别,inode都指向同一个文件的路径
软链接 符号链接
也就是快捷方式 保存了代表的文件的绝对路径,是另外一种文件
概念
一种文件共享的方式
12.操作系统设计
11.window
9.安全
8.多处理机系统
7.多媒体操作系统
10.Linux
栈
linux内核
进程栈
用户态栈
线程栈
Linux把所有线程都当做进程来实现
进程内核栈
每个进程的生命周期中,必然会用系统调用进入到内核,所以在内核态中,调用方法时使用内核栈
中断栈
当系统发生中断事件进行中断处理时,使用进程中断栈
Linux的系统结构????
内核
内存管理
进程管理
shell
shell是系统的用户界面,提供用户与内核进行交互操作的接口
文件系统
应用程序
用户态 内核态
内核态和用户态的区别
用户态:所有应用程序运行在用户态
内核态:内核、驱动相关的事务,如访问硬件设备
为什么要区分用户态和内核态?
因为有多个进程以后,对于关键资源就会产生竞争和误操作破坏关键资源的可能,就需要对资源的访问权限进行限制
内核态具有最高的访问权限
用户态访问核心资源时必须切换内核态才可以访问
用户态和内核态的切换的方式
系统调用
用户态进程主动要求切换
异常
缺页异常
外围设备的中断
外围设备完成用户请求的操作后,会向cpu发出中断信号
在用户态中实现线程的优缺点和在内核态中实现线程的优缺点
用户态
用户态中,启动线程比进行系统调用的效率更高, 不需要切换到内核,不需要上下文切换
容易发生缺页中断问题
内核态
很容易检测缺页故障
创建线程或销毁线程开销大
什么操作会陷入内核态?new一个线程会不会陷入内核态?
系统调用
不会,进程运行于用户空间。通过调用库函数来和内核通信,只有当进程需要访问物理硬件设备时,才需要陷入内核态
linux的进程
进程创建
概念
由已存在的进程通过fork创建出来,最后形成一个树的结构
fork其实是把父进程复制一份,子进程和父进程共用程序代码,共用时间片
父进程的程序代码
但子进程有自己的标识、状态、数据空间
系统堆栈区
数据区
什么是0号进程、1号进程、2号进程
0号进程
算是个链表头,创建出1号和2号
1号进程
1号进程是所有用户态的祖先
2号进程
是内核态所有进程的祖先
Linux命令
查看系统当前进程
ps -ef
UID是用户的标识符,比如当前用户名为ysd,则ysd创建的进程UID都叫ysd
PID是当前进程的id
PPID是当前进程的父进程的id
Linux命令执行过程????
6.处理机调度和死锁
5.IO输入输出
IO模型
阻塞IO(同步IO BIO)
概念
发起一次IO操作后将会一直等待成功或失败后才返回,在此期间程序不能做其他事情
应用
只能对单个文件描述符进行操作
阻塞IO、非阻塞IO、同步IO、异步IO的区别
非阻塞IO (同步非阻塞IO NIO)
概念
通常发生在一个for循环,。每次IO要么成功要么操作发生阻塞返回错误,然后再进行下一次for循环,这种“轮询”浪费很多不必要的CPU资源
应用
只能对单个文件描述符进行操作
IO多路复用
概念
首先对一组(多个)文件描述符进行相关事件的注册,然后阻塞等待某些事件的发生或等待超时
事件的发生跟中断很像,安卓的DataBinding也是基于事件???
内核一旦发现进程指定的一个或者多个IO条件准备读取,他就通知该进程
Linux下的IO多路复用
select
poll
epoll
区别
好处
与多线程,多进程相比,IO多路复用优势是系统开销小,不用创建进程线程
应用场景
客户处理多个描述字
客户同时处理多个套接字
TCP服务器既要处理监听套接口,又要处理已连接套接口
服务器既要用TCP也要UDP
服务器要处理多个服务或多个协议
信号驱动IO
概念
利用进程通信方式中的“信号”,让内核告知应用程序文件描述符的相关事件
应用
TCP连接建立
一方断开TCP连接请求
断开TCP连接请求完成
TCP连接半关闭
数据到达TCP socket
数据已经发送出去(如:写buffer有空余空间)
异步IO
异步IO是 IO操作不会阻塞当前程序的继续执行 同步IO是程序会一直阻塞到IO操作的完成
概念
和信号驱动IO相似,相比信号驱动IO需要在程序中完成数据从用户态到内核态的拷贝,异步IO可以把拷贝这步帮我们完成后再通知应用程序
中断和轮询
轮询:cpu对特定设备轮流访问
中断:通过特定事件提醒cpu
中断的处理过程
保护现场
开中断
中断处理
关中断
恢复现场
1.概念
特性
并发
并行和并发区别
并行是指有多个处理器,cpu一起工作,实现同一时间点内有多个线程正在执行
并发是指在一个处理器同一时间段内,来回切换线程执行,形成看起来有多个线程正在同时进行的现象
共享
系统的资源可供内存中多个进程同时使用
虚拟
将1个物理实体变为若干个逻辑物体
比如虚拟内存MMU
时分复用,空分复用
异步
资源占用导致进程执行走走停停
分类
批处理系统
从一个程序切换到另一个程序被称为上下文切换
实时系统
分时系统
网络操作系统
组成部分
处理机管理
进程控制
进程同步
进程通信
调度
存储管理
内存管理机制
连续分配管理
块式管理
将内存分为几个固定大小的块,这样极大地浪费了空间 块>页>段
非连续分配管理
页式管理
把内存分为固定且大小相等的一页一页的 逻辑地址和物理地址
段式管理
每一段都有其实际的意义 程序段、数据段、代码段
分页和分段的共同点和区别
共同点
区别
段页式管理
把主存分为若干页 再分段
内存管理是干嘛的
操作系统管内存的申请 释放
地址映射
文件管理
文件存储空间的管理、目录管理 、文件操作管理、文件保护
设备管理
设备分配、设备传输控制 、设备独立性
接口
用户接口
程序接口
结构
宏内核
kernel和5个组成部分结合在一起
微内核
只有kernel,用户需要什么就通过kernel调用什么
外内核
系统调用
用户空间
每个进程都拥有部分用户和内核空间
内核空间
https://blog.csdn.net/ly0724ok/article/details/104737363
计算机网络
网络
网址URL
网址除了http:用于访问web服务器 还有 ftp: 在ftp服务器上传下载 file:等等 具体访问什么服务器就用什么为网址前缀
web浏览器
生成HTTP请求消息
向DNS服务器查询web服务器的ip地址
全世界DNS共同查询web服务器的ip
委托协议栈发送消息
协议栈,网卡
集线器,交换机,路由器
接入网,网络运营商
防火墙,缓存服务器
web服务器
概述
网络分层
应用层
域名系统DNS
DNS解析过程
DNS解析在网络不好的情况下怎么办?
DNS解析请求的服务器单机并且响应速度慢怎么办
电子邮件
http
超文本传输协议
UDT
ftp
文件传输协议
smtp
简单邮件传输协议
pop3
邮件传输协议
传输层
TCP
打电话:建立稳定的连接
UDP
发信息,无法确认对方一定收到
协议层
网络层
IP
IP地址
端口号
用来识别同一台计算机中进行通信的不同应用程序,也就是程序地址
ICMP
路由选择协议
网络层和传输层的关系是什么?
网络接口层
数据链路层
物理层
七层协议
应用层
表示层
LPP
XDP
会话层
SSL TLS
RPC
传输层
TCP
UDP
网络层
IP
数据链路层
ARP
物理层
IEEE
网络安全
概念
原理
加密方式
对称加密
有哪些????
非对称加密
有哪些????
应用
SYN洪泛攻击
在三次握手的第一次握手,客户端发给服务端的请求ip是假的,这样服务端就永远发不到客户端的ip,就会一直等待第三次握手,阻塞下去
若不断发送伪造原地址的ip,就会造成半开连接队列被占满,服务器无法被其他客户端连接
解决方案
监控无效连接,并释放他们
延缓TCB分配方法
设置防火墙阻挡大量攻击报文的进入
DNS劫持
劫持了DNS服务器,获取某域名的解析记录控制权,从而修改该域名的解析结果,使得客户端访问了假的网址,从而窃取资料
HTTP劫持
劫持客户端和服务端之间专用的数据通道,监视特定数据信息,当满足条件,弹出新窗口展示广告
DDOS和中间人攻击
SQL注入
XSS攻击
域名解析DNS
概念
原理
有哪些解析方式?
应用
DNS的作用
HTTP
HTTP是应用层协议 是不保存状态的协议
概念
与HTTP有关的组件
与HTTP有关的协议
HTTP请求
HTTP连接过程
1.客户端连接上服务端 结束请求后,由客户端或者服务端进行HTTP连接的关闭
2.下次再发送请求的时候 客户端再发起一个连接,传送数据,关闭连接
HTTP的Keep-Alive
概念
功能:减少新建和断开TCP连接的消耗
短时间内连接复用,希望可以短时间内在同一个连接上进行多次请求/响应
如何避免进行重复的TCP三次握手和四次挥手
原理
客户端发送connection:keep-alive头给服务端,且服务端接收这个头,这个连接就可以复用
请求码构成
请求体
请求首部
HTTP响应
HTTP状态码
1xx
信息性状态码
临时响应 接收的请求正在处理
100
请客户端继续
101
服务器正在更换协议
2xx
成功状态码
请求处理完毕
200 OK
请求没问题
201
请求成功,服务器已创建好资源(如put)
202
服务器已接收请求但未处理
203
服务器已处理但返回的响应来自另一服务器
204 No Content
服务器已处理,但不返回任何内容
205
服务器已处理,但不返回任何内容 , 且要求请求者清除表单内容输入新内容
206 Partial Content
服务器已处理部分GET请求
3xx
重定向状态码
需要进行附加操作以完成请求
300
客户端请求一个实际指向多个url资源时会收到
301 Moved Permanently
请求的url已被移除时会收到,响应的Location首部包含资源现在所处的url
302 Found
http1.0
客户端发起POST请求,并在响应中收到302时,会接受location首部的url,并向它发起GET请求
303 See Other
http1.1
客户端发起POST请求,并在响应中收到303时,会接受location首部的url,并向它发起GET请求
304 No Modified
当客户端发起GET请求
且当资源未被修改时,会响应它表示资源未被修改 如上次请求的网页未修改过
305
向客户端说明必须通过一个代理来访问资源 代理位置在location首部
306
未使用
307
http1.1
和303类似,但不会自动将POST转GET,而是让用户自己去点新的url
4xx
客户端错误
服务器无法处理请求
400 Bad Request
客户端发送了错误的请求
401 Unauthorized
客户端还未认证服务器
402
保留
403 Forbidden
客户端的请求被服务器拒绝
404 Not Found
服务器无法找到所请求的url
405
客户端发起的请求带有url不支持的方法
406
服务器没有和客户端可接受url相匹配的资源
有url资源,但加了条件就没有
407
客户端还未认证代理服务器
408
客户端完成请求用时太长
409
请求可能引发资源冲突
5xx
服务端错误
服务器处理请求出错
500 Error
服务器遇到错误,无法为该请求提供服务
501
客户端发起的请求超出服务器能力范围
使用了服务器不支持的请求方法
502
代理服务器收到一条伪响应
503 Service Unavailable
服务器现在无法为请求提供服务
504
服务器等待另一个服务器的响应超时了
505
客户端使用了服务器不支持的协议版本 如http2.0版本
子主题 6
https://cloud.tencent.com/developer/article/1688459
https://segmentfault.com/a/1190000018264501
响应码构成
响应体
请求首部
HTTP报文
HTTP标头
HTTP内容协商
HTTP缓存
强缓存
不会向服务器发送请求,直接从缓存中读取资源
共同点 都是从客户端缓存中读取资源
协商缓存
向服务器发送请求,服务器根据请求的request header的参数判断是否命中协商缓存,如果命中就返回304状态码并带上新的reponse header通知浏览器从缓存中读取资源
区别是强缓存不会发请求,协商缓存会发请求
HTTP认证
区别
HTTP和socket的区别
http的报文包含什么?
报文头部有什么内容?
上传一个文件是如何上传的?
幂等
一个http方法是幂等的,同样的请求被执行一次和连续执行多次返回的结果都是一样的
幂等性只和后端服务器实际状态有关 幂等说明服务器稳定
原理
HTTP1.0 和HTTP1.1的区别
增加缓存控制
HTTP1.0 使用header里的expires 、if-modified-since
HTTP1.1 引入更多的缓存控制策略 header里的 entity tag、If-Match
请求
连接方式
HTTP1.0短连接
每次请求都要重新建立一次连接
HTTP1.1长连接
能维持一个长连接,来发送多个请求
默认开启keep alive
支持管道传输
第一个请求发出去不必等响应就可继续发第二个请求
传输和连接
增加状态码
HTTPS增加了24个错误状态响应码
响应
HTTP2.0和HTTP1.1的区别
头部压缩
协议会帮你消除多个请求中头部重复的部分
二进制格式
不再采用HTTP1.1的纯文本报文,而是全二进制,计算机不必文本转二进制而是直接解析二进制,增加了效率
客户端
数据流
每个请求的所有数据包,称为一个数据流,每个数据流都有编号
传输时
多路复用
一个连接中并发处理多个请求或回应而不是按照顺序先处理完A再处理完B
服务器推送
服务器不再是被动响应,也可以主动向客户端发送消息
服务端
应用
http请求码
put
上传文件 增
delete
删除文件 删
post
传输数据 改
get
获取服务器资源 查
GET和POST有什么区别
应用
get用于搜索 排序和筛选
post用于修改和写入数据
参数格式
GET把需要的参数写在URL上
POST把需要的参数写在request body内
安全
post更安全 因为其不会作为url的一部分,不会被缓存保存在服务器日志及浏览器浏览记录
但由于HTTP是明文传输,所以实际上都不安全
速度
post比get速度慢
数据量
post能向服务器发送更大的数据,get有url长度限制
post能发送更多的数据类型,get只能发送ASCII字符
HTTPS
https连接过程
1.客户端给服务器发送一个请求
2.服务器发送SSL证书给客户端
证书的发布机构、有效期、所有者、签名、公钥
3.客户端对公钥进行真伪校验,校验为真就使用公钥对称加密算法以及公钥进行加密
4.服务端使用私钥解密,用对称密钥加密确认信息发给客户端
非对称加密
公钥加密 私钥解密
5.客户端和服务端使用”对称密钥“进行信息传输
对称加密
HTTPS的四次握手(SSL的连接过程)
SSL连接
1.客户端发请求
2.服务端发SSL证书+公钥
3.客户端发公钥加密的密文
4.服务端发确认
HTTP连接
先进行SSL 再 http 最后才tcp三次握手??
TCP连接
https协议的实现过程
用了哪些证书
HTTPS和HTTP的区别
端口
HTTP的URL由http://起始 默认使用80
HTTPS的URL由https://起始默认使用443
安全性
对称加密 同时对称加密的密钥使用服务器的数字证书进行非对称加密
资源消耗
https消耗更多服务器资源
协议的区别
https多了一层SSL(位于TCP和HTTP之间)
HTTPS的四次握手
https的过程、一共有几次加密?
使用HTTPS一定能保证安全吗? https为什么不能保证绝对安全?
cookie、session、token
概念
应用
session
通过服务端记录用户的状态
将商品加入购物车,
cookie
在客户端(浏览器端)保存用户信息
某些网站记录下上次填写的账号密码,这次再用就能自动填写
socket传输原理
流程???
ip
子网掩码的作用
屏蔽IP地址的一部分以区分网络标识和主机标识
将大的IP网络划分为若干小的子网络
有了MAC为什么还要ip?
IP是邮政编码,和地域相关
MAC只是ID 和地域无关
IP地址不能去掉 路由器通过IP地址前缀可以知道设备在哪个子网上,而MAC不能
MAC也不能去掉 IP地址要设备上线后才能根据进入了哪个子网来分配,如果没有IP地址,就要通过MAC来区分不同设备
路由器的转发机制
路由器先从路由表查找
如果不知道如何发送分组就丢弃,否则就发送到下一个站点
URL
相似问题:客户端输入http:/android之后经历了哪些层?各种协议和HTTP协议之间的关系
1.客户端向DNS服务器询问目标服务器地址
2.客户端的HTTP协议生成请求报文
3.客户端的TCP协议将HTTP请求报文分割成报文段,按序号可靠的发给对方
是先建立三次握手的连接还是先进行HTTP请求?
4.客户端的IP协议搜索服务器地址,一边中转一边传送
5.服务器的TCP协议接收并重组拼接报文段
6.服务器的HTTP对请求报文进行处理
7.以同样利用TCP/IP协议向用户回传
URL到弹出网页会经历什么 从应用层说到数据链路层
DNS解析
TCP连接
发送HTTP请求
服务器处理请求并返回HTTP报文
浏览器解析渲染页面
连接结束
分析一个url链接
UDP
概念
TCP和UDP区别
是否面向连接
TCP建立3次 断开4次 断开连接时发起方可能进入TIME_WAITED状态长达数分钟,端口无法释放 只能一对一传输
UDP不需要建立连接 支持多对多传输
传输可靠性
TCP利用握手、重传机制 可靠 (8个可靠性)
UDP不可靠
传输形式
TCP面向字节流
应用层给TCP多少,TCP会把数据进行合理分包
UDP面向报文
应用层给UDP多少,UDP就发多少
性能
TCP效率低,资源消耗多
UDP效率高,资源消耗少
应用场景
TCP 文件传输 邮件 远程登录(准确度要求高,速度可以慢)
HTTP HTTPS FTP
UDP qq聊天 在线视频 网络电话 (准确度要求低,速度要求快)
DNS NFS TFTP 音视频通信
首部字节
TCP
长度最小20个字节,最大60个字节
源端口、目的端口、32位序号(请求号syn)、32位确认号、4位头部长度、6位保留、控制位、16位窗口大小、16位校验和、16位紧急指针、选项部分(最小0字节,最长40字节)
UDP
长度8个字节
源端口号、目的端口号、数据报长度、校验和
原理
应用
怎么样让UDP传输可靠?
传输层无法保证
应用层可参照tcp可靠性传输
实现确认机制、重传机制、窗口确认机制。
如果你不利用Linux协议栈以及上层socket机制,自己通过抓包和发包的方式去实现可靠性传输,那么必须实现如下功能:
发送:包的分片、包确认、包的重发
接收:包的调序、包的序号确认
目前有如下开源程序利用udp实现了可靠的数据传输。分别为RUDP、RTP、UDT。
TCP
概念
TCP报文有哪些内容???
TCP详解
原理
三次握手
过程描述
状态描述
1.客户端进入SYN_SENT
2.服务端SYN_RECV
3.双方都进入ESTABLISED
为什么要三次握手?
验证客户端的发送接收、服务端的发送接收的功能
三次握手连接阶段,最后一次客户端向服务端发送的ACK包丢失,会发生什么?
服务端
服务端会根据超时重传机制,等待3 6 12秒后重新发送SYN+ACK包
如果重发指定次数仍然没收到客户端ACK, 过一段时间后服务端就自动关闭这个连接
客户端
客户端认为连接已经建立,如果客户端向服务端发送数据,服务端就会返回RST响应,客户端就知道服务端没收到ACK包了
四次挥手
过程描述
四次挥手中间状态的变化对应的缓冲队列
TCP的KeepAlive
概念
KeepAlive的作用是保持客户端和服务端的连接
为了保活,心跳,检测连接错误,当连接两端长时间没有数据传输时,发送KeepAlive 探测连接是否存活
原理
一方会不定期发送心跳包给另一方表示我还没断开连接呢,虽然我现在没发数据给你
当其中一方断掉,没有断掉的另一方定时发送好几个心跳包,对方依然返回RST,而不是ACK 那就会释放当前连接
状态描述
Keep-Alive字段是什么意思?
为什么要四次挥手
将三次握手中的第二次拆成2次,为了让服务端发完数据
time_wait状态有什么用
time_wait是客户端等待或者服务端等待对方的一个状态
TIME_WAIT出现在连接的哪个阶段??
TIME_WAIT状态连接过多如何处理??
长连接会有什么问题??
通用的连接池怎么实现?
TCP可靠传输
应用数据被分割成适合发送的数据包
发送方对每个包进行编号,方便接收方排序
校验和 :保持包内的首部和数据的检验和
接收端会丢弃重复发送的包
流量控制
可变大小的滑动窗口协议
概念
在tcp协议中,如何保证发送方和接收方之间每个包都能被收到并且是按照次序来的?
发送方发序列号为1号发送包,接收到1号确认包后再发2号发送包
如何提高吞吐量
发送方一次性发2个发送包,等到收到2个确认包后再发2个
如何实现最优解
发2个包,收到1个包过后就把第3个包发过去
流量控制是作用于接收者的,它是控制发送者的发送速度从而使接收者来得及接收,防止分组丢失的
原理
滑动窗口固定大小为7
每个包都是数组的元素,一个元素有4种状态:已发送且已ack,已发送未ack , 待发送,未发送
滑动窗口内有 “已发送未ack” “待发送”两种包,当窗口内最左边的包已ack后,窗口整体右移,去除最左边的包,添加一个待发送的包
滑动窗口每隔一点时间就将“待发送”发送,变为“已发送未ack”
直到滑动窗口里全是“已发送未ack“,就不能右移,此时会采取超时重传
拥塞控制
概念
避免网络环境恶化,造成你收不到我就使劲发,导致网络线路更加拥堵的恶循环
一个往返时间RTT就是一个传播轮次
cwnd(拥塞窗口)初始值为1
乘法减小
cwnd设置为ssthresh(遇到阻塞时的cwnd的大小)的一半的值
“乘法减小”指的是无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞,就把慢开始门限ssthresh设置为出现拥塞时的发送窗口大小的一半,并执行慢开始算法
原理
慢启动
每经过一个往返时间就cwnd*2
拥塞避免
每经过一个往返时间就cwnd+1
快重传
发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段
判断收到3个重复确认时,发送方可认为出现拥塞,快重传后下一步采用快恢复
快恢复
cwnd直接从新的ssthresh开始,继续拥塞避免
新的门限值是原来门限值的-4
老版本TCP
慢启动->拥塞避免->乘法减小从慢启动开始->拥塞避免
新版本TCP
慢启动->拥塞避免->遇到阻塞就乘法减小但cwnd从ssthress的一半开始(快恢复)->拥塞避免
ARQ协议(自动重传)
发完一个分组就停止发送,等待对方确认,收到确认就发送下一组,如果“一段时间内”没收到确认帧,就自动重传
超时重传
一定时间内不收到就重发
应用
序列号怎么生成?
序列号怎么变化的?随着确认接收?
TCP的头部有什么?
断开连接,谁进入TIME_WAIT状态,谁进入CLOSE_WAIT状态
发起方进入TIME_WAIT,接收方CLOSE_WAIT
自由主题
302 303 307的共同点都是让客户端去请求重定向url
HTTP连接过程
ARQ和超时重传区别??
自由主题
计算机组成原理
编译型语言
预编译
define
.h 各种库
编译
静态编译
static
动态编译
一个.c内的所有代码
链接
多个.c .h要链接到一起
运行
不带代码的可执行文件
解释型语言
带有代码的可执行文件
一边解释一边执行
python
进制
反码
补码
print("hello world")是怎么显示到屏幕上的??
先编译生成hello文件,./hello运行,键盘输入后 通过io总线送到io桥,再送入cpu,cpu的alu计算后通过DMA将代码从磁盘加载到内存 ,让cpu和内存进行运算,最后运算结果到达显示器显示出来
静态链接库和动态链接库的区别?
动态链接库
动态链接库是一个可以被其它应用程序共享的程序模块,其中封装了一些可以被共享的例程和资源。动态链接库文件名的扩展名一般是dll,动态链接库中虽然包含了可执行代码却不能单独执行,而应由应用程序直接或间接调用
动态链接就是把调用的函数所在文件模块(DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因此需要相应DLL文件的支持
概念
有lib文件
lib文件里包含函数所在的DLL文件中的位置入口
代码在运行时由加载DLL来提供
静态链接库
静态链接是指把要调用的函数或者过程链接到可执行文件中,成为可执行文件的一部分。换句话说,函数和过程的代码就在程序的可执行文件中,该文件包含了运行时所需的全部代码。
静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件。
概念
有lib文件
lib文件包含函数代码本身
编译时就把代码加入程序中
概念
可执行模块
.dll文件
.exe文件
头文件
.h
库文件
.lib
静态链接、动态链接?
程序怎么加载到内存的?
计算机是怎么将程序运行起来的
代码从编写到运行的过程?
编译这个过程从.i文件到汇编代码具体是怎么个过程??
编译
将我写的代码.java,编译成二进制文件.class
链接
将二进制代码文件.class链接起来,生成可执行文件
运行
运行可执行文件
Java代码块编译顺序
预编译
父类静态代码块
子类静态代码块
编译
父类代码块
父类构造器
子类代码块
子类构造器
线程
为什么要引入锁
两个线程都同时访问同一个资源,如果不加任何的锁,会造成读写错乱
目的是为了将资源在同一时间内,只允许一个线程使用它。也就是同步的概念
线程同步与死锁相关问题
为什么要同步?
买票卖票问题,多个线程分别卖票,但是票都卖光了,另一个线程还在卖
同步
synchronized
synchronized()括号里代表锁住某个资源的意思,此时其他线程不能使用括号内的资源
代码块
方法
什么是死锁?
我想要你的钱,你想要我的物品。我说你先把你的钱给我我再给你物品,你说我先把物品给你再给我钱
有两个或多个线程,他们各自占有对方所需资源,同时又需要请求获得对方手中的资源才能释放手中的资源的现象
模拟死锁
死锁条件 互斥使用:一个资源只能分配给一个线程 不可剥夺:资源只能由占有者释放,申请者不能强制剥夺 请求保持:线程申请资源时,保持对原有资源的占有 循环等待:存在一个进程等待队列:{P1 , P2 , … , Pn}, 其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路 代码 思路 定义两个资源o1,o2 对象deadLock1占有资源o1,需要资源o2 对象deadLock2占有资源o2,需要资源o1 死锁产生
死锁的四个条件
互斥条件
请求与保持条件
不剥夺条件
循环等待条件
首先资源之间必须是“互斥”的,线程一旦阻塞,就会“请求与保持”该状态,且其他线程“不能剥夺”该线程手上的资源、最终导致多个线程“循环等待”
如何预防死锁
如何避免死锁
提前预防
仔细对资源进行动态分配,避免产生死锁
鸵鸟算法
处理
检测死锁并恢复
破坏其中一个条件
如何破坏死锁
破坏死锁的四个条件
破坏锁的互斥
做不到,本身锁就是互斥锁的话没法破坏
破坏线程的请求与保持
让一个线程一次性申请所有资源而不是一个个申请
synchronized(所有资源)
破坏线程的循环等待
每个线程按照顺序来申请资源
使用信号量计数,取余
破坏线程的不剥夺
线程申请不到所需资源后,要主动放弃持有的资源
设置个时间,超过时间后中断线程
生产者和消费者是什么?
生产者set标题和内容,消费者get标题和内容
数据错位
synchronized
重复操作
标志位+wait+notify
锁
概念
分类
公平锁/非公平锁
是多个线程是否按照申请锁的顺序获得锁
synchronized非公平
reentrantlock非公平(也可以设置为公平)
可重入锁/不可重入锁
可重复可递归调用的锁 线程可多次进入该锁 (锁可以是方法,能够递归调用该“方法”的这样的锁就是可重入锁)
synchronized修饰方法
ReentrantLock
在AQS中维护一个volatile int state计数重入次数,实现了可重入
总结一下这两个的本质区别 AQS和CAS的区别
不可递归调用,一旦递归调用就发生死锁
直接使用原子类 原子Integer 原子Reference是不可重入,需要使用state计数来设置可重入锁
独享锁/共享锁
他们也是通过AQS实现的
独享锁:该锁每一次只能被一个线程所持有。
互斥锁/读写锁
互斥锁:一个锁一次只能被一个线程所持有
乐观锁/悲观锁
分段锁
偏向锁/轻量级锁/重量级锁
自旋锁
问题
说一下你了解的锁的定义??
锁的解释
锁是一个名词,代表一个资源,如进入到synchronized(this) 代码后,表示线程持有锁,该锁就是当前对象
应用
属性锁
线程锁
属性锁
CAS原子操作类
乐观锁 原理:比较并交换 Compare And Swap 认为程序中并发情况不是很严重,让线程不断尝试更新资源
概念
比较并交换
乐观锁
原子引用
原理
原子类的对象本质全都是使用volatile对各种数据类型进行修饰声明
应用
基本类型
AtomicInteger
AtomicLong
引用类型
AtomicReference
数组类型
AtomicIntegerArray
volatile
概念
volatile的三个特性??
保证变量的内存可见性
当一个线程修改某变量,对其他线程是可见的
禁止指令重排序
因为不会引起线程上下文切换和调度,所以是轻量级锁
可见性
当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能立即看到修改的值
例子:每个线程都会在运行过程中将变量拷贝到自己的工作内存,当修改值以后还没写入主存时,其他线程若获得时间片,将会对主存中的原来的值进行拷贝
例子:修改和写入主存两个能同时一起执行
volatile如何实现可见性?
1.使用volatile会强制将修改的值立即写入主存
2.使用后,当线程2进行修改时,会导致线程1的工作内存中缓存变量的缓存行无效
只有发生了修改才会让缓存变量的缓存行无效
3.由于线程1的工作内存中的缓存行无效了,所以又会再次读取主存中的值
(2和3的操作是给1多上了个保险)
原子性
就是不可分割、完整性,即某个线程正在进行某个任务的时候,中间不可以被加塞或者被分割,需要整体完整,要么同时成功,要么同时失败
例子:a++ 有三个操作,读取到工作内存,修改,写入主存,原子性能三个同时一起执行
volatile能保证“每次读取的是最新的值“,但没法保证对“变量的操作”是原子性
即能保证 “修改+写入”操作是一起的,但三个操作不能保证全部一起
有序性
volatile能禁止指令重排序,所以能在“一定程度”保证有序性,不是绝对保证有序性
原理
lock前缀:内存屏障
1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;
2)它会强制将对缓存的修改操作立即写入主存;
3)如果是写操作,它会导致其他CPU中对应的缓存行无效。
控制层面
内存和CPU高速缓存
当线程执行时,会先从主存当中读取值,然后复制一份到高速缓存当中,然后CPU执行指令操作,然后将数据写入高速缓存,最后将高速缓存中i最新的值刷新到主存当中。
控制机制:控制主内存和CPU高速缓存的一致性,当一个线程更改了某个内存变量时,会强制更新主内存,并通知其他CPU从主内存中重新获取变量值。
应用
int a=b 是不是线程安全?
不是,原因是因为b和a没有被volatile修饰,会发生指令重排序,编译时a=b被分为三段语句执行
volatile可以用在哪些地方
状态标志位 结束循环
一次性发布
线程锁 线程间同步的方法
synchronized+wait、notify
可重入锁 同步锁 悲观锁 synchronized就是给对象上锁,在内存的层面上就是给对象加标志位。表示当前对象已上锁,其他想要使用该对象的线程需要等待。 锁就是标志位,上锁就是给对象上一个不能被其他线程访问的标志位,释放锁就是取消掉给对象上的标志位 1、普通方法加synchronized 虽然method1和method2是不同的方法,但是这两个方法都进行了同步,并且是通过同一个对象去调用的,所以调用之前都需要先去竞争同一个对象上的锁(monitor),也就只能互斥的获取到锁,因此,method1和method2只能顺序的执行。 2、静态方法加synchronized 虽然test和test2属于不同对象,但是test和test2属于同一个类的不同实例,由于method1和method2都属于静态同步方法,所以调用的时候需要获取同一个类上monitor(每个类只对应一个class对象),所以也只能顺序的执行。 3、代码块加synchronized+this 对于代码块的同步实质上需要获取Synchronized关键字后面括号中对象的monitor,由于这段代码中括号的内容都是this,而method1和method2又是通过同一的对象去调用的,所以进入同步块之前需要去竞争同一个对象上的锁,因此只能顺序执行同步块。 悲观锁 认为程序中的并发情况严重,所以严防死守
两个线程分别使用两个不同的资源
cpu分配时间片给线程1,线程1的代码:synchronized(A) 此时对象A未上锁,可以执行代码块内部 当代码块还未执行完,CPU可能会分配时间片给线程2,这时线程2的代码:synchronized(B) 进入代码块内部 。 轮到哪个线程执行,是cpu时间片分配的事情 至于线程能不能执行这条语句synchronized(A) ,则是加锁的事情
两个线程同时使用1个相同的资源
cpu时间片分配和添加synchronized的关系 时间片是时间片,加锁是加锁,两者没有关系 cpu任意分配时间片给任意一个线程,并不是说线程1加了synchronized就意味着要执行完,线程2才有机会拿到时间片。而是因为线程1它占有着线程2也想访问的资源A,所以同一时间内只能有一个线程访问资源A,synchronized(A)会一直执行下去直到释放掉资源A 线程2才有机会再次给资源A加锁 如果你想让线程1执行完,线程2才有机会执行而不通过对同一资源进行加锁的方式,你可以采用join()方法
两个线程都想同时使用两个不同的资源
比如线程1给资源A加完锁,然后sleep10秒,这段时间cpu把时间片分给了线程2,线程2给资源B加完锁,也睡觉10秒。 线程1先睡醒,然后想访问资源B,这时因为资源B被上锁,无法访问,所以在cpu分配给线程1的时间片内,线程1无事可做,什么都没干,是一种极大的浪费(重量级锁) 然后线程2也醒了,它想访问资源A 可资源A被加锁,也不能执行下去了。整个程序陷入到死锁 死锁代码 public class sisuo { static Integer source1 = new Integer(100); static Integer source2 = new Integer(200); public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { synchronized (source1){ System.out.println("我是线程1 我抢到了source1"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (source2){ System.out.println("我是线程1 我拿着source1和source2"); } } } }).start(); new Thread(new Runnable() { @Override public void run() { synchronized (source2) source2=222; System.out.println("我是线程2 我抢到了source2,但我没有给source2加锁"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (source1){ System.out.println("我是线程2 我拿着source1和source2"); } } }).start(); } }
原理
synchronized( )的解释
对括号内表达式求值(必须是引用类型),获取它所代表的的对象,尝试获取这个对象的锁
如果能获取锁,则进入同步块执行,执行完后退出同步块,并归还对象的锁(异常退出也会归还);
如果不能获取锁,则阻塞在这里,直到能够获取锁。如果一个线程在同步块中,则其他想进入该同步块的进程被阻塞,处于该同步块的Entry Set中,处于BLOCKED状态。
synchronized的四种状态
无锁
不锁住资源,多个线程只有一个能修改资源成功,其他线程会重试
尚无线程执行到同步代码块
偏向锁
同一个线程获取同步资源时,没有别人竞争时,去掉所有同步操作,相当于没锁
线程1执行到同步代码块,虚拟机会使用CAS尝试修改状态标志位,修改为偏向锁状态,并且把线程1的线程ID记录到markword区域的23bit位,进入偏向锁状态
进入偏向锁状态后,如果没有其他线程竞争,线程1后续再次访问同步代码块时,犹如没有锁一样,jvm不会再进行CAS加锁、解锁等步骤,直接运行同步块代码。直到线程1执行完毕后,jvm会释放偏向锁,将markword的标识位恢复初始状态。
轻量级锁
多个线程抢夺同步资源时,没有获得锁的线程使用CAS自旋等待锁的释放
线程2访问同步代码块,尝试获取锁;此时jvm会检查线程1的状态,因为线程1还持有锁,jvm不能撤销线程1的锁,此时,jvm就会把锁升级位轻量级锁,也就是这个23bit区域存了线程1的地址,指向线程1的线程栈中的某块区域;同时线程栈的这块内存也保存了指向markword的引用,相当于两块区域互换了内容
重量级锁
多个线程抢夺同步资源时,使用操作系统的互斥量进行同步,没有获得锁的线程阻塞等待唤醒
进入轻量级锁状态后,线程2还会继续自旋尝试获取锁;这个时候,synchronized并不会立即进入重量级锁状态,而是等到线程2 自旋达到一定次数后,jvm才膨胀为下图的重量级锁。这个自旋次数,jdk7及以后可以通过jvm参数设置。
线程1执行完同步代码块,jvm尝试释放锁,修改markword为初始的无锁状态,在释放锁的时候,发现已经是重量锁了,说明有其他线程竞争,并且其他线程肯定已经进入了阻塞状态,那么jvm在释放锁之后,还会唤醒其他进入阻塞状态的线程
synchronized锁提供的两种特性?
互斥 一次只有一个线程能够使用该资源
可见性
不保证有序性 即允许指令重排序
控制层面
JVM
控制机制:控制某个方法的执行者或者是某个变量的拥有者。如果是方法锁,则同一时间只能有一个线程使用该方法,其他线程阻塞;如果是对象锁,则哪个线程最先拥有锁对象,则哪个线程先执行,其他线程阻塞
应用
类锁
对象锁
方法锁
问题
sychronize、volatile作用?
sychronize和Lock区别是什么?
synchronized修饰的方法和static synchronized修饰的方法有什么区别?
锁普通方法和锁静态方法的区别就是
锁普通方法是锁当前对象
锁静态方法是锁类
synchronized为什么效率低下?
synchronized底层是monitor监视器锁,而监视器锁的实现依赖操作系统Mutex Lock ,Java线程是映射在操作系统线程之上,切换线程需要从用户态到内核态的陷入
Lock接口
概念
ReentrantLock类
概念
ReentrantLock和synchronized的区别
相同
两者都是可重入锁
一个线程可以多次获取同一把锁
不同
synchronized依赖于jvm
jvm层面去控制只能有一个线程访问该资源
ReentrantLock依赖于CPU
CPU层面 在总线下只允许一个线程访问某内存
API的底层原理是volatile
需要lock()+try
synchronized会自动释放锁,ReentrantLock需要手动释放unlock
提供公平锁和非公平锁两种办法
reentrantLock通过FIFO队列管理所有等待线程
是通过AQS实现线程调度的
原理
原理
控制层面
CPU总线
控制机制:控制线程的执行的CPU,在总线之下只允许一个线程的CPU可以访问某个内存,当某个线程执行结束之后其他线程才能访问变量资源
应用
ReadWriteLock接口
ReadLock
在数据读取时使用共享锁
WriteLock
在数据修改时使用互斥锁
并发问题
int a; a++
原子操作,a++分为三个过程
读取a的值
++操作
写入a的值
多线程并发时,有可能第一个线程获取的a=10,即将++,这时另一个线程获取a,获取的也是10,当他们执行完后,本来是12,但实际上结果是11(写入a的值都是11) 所以就要保证a++这个过程不能被拆开 即这个操作是同步的
synchronized同步代码块
CAS原子类工具
AtomicInteger int a
lock锁机制
Lock lock=new ReentrantLock()
fun(){ lock.lock() ; try{a++;}finally{lock.unlock()}}
线程安全问题
线程不安全:多线程并发执行某个代码时,产生了逻辑上的错误,结果和预期值不相同
线程安全是指多线程执行时没有产生逻辑错误,结果和预期值相同
死锁、活锁、饥饿
活锁
出现了线程礼让现象 线程1和线程2都需要获取一个资源,但他们同时让其他线程先获取该资源,两个线程一直谦让,最后都无法获取
死锁
活锁是不断尝试,死锁是一直等待;活锁有可能自行解开、死锁无法自行解开
饥饿
操作系统进程调度算法的问题 一个或者多个线程因为种种原因无法获得所需要的资源, 导致一直无法执行的状态。以打印机打印文件为例,当有多个线程需要打印文件,系统按照短文件优先的策略进行打印,但当短文件的打印任务一直不间断地出现,那长文件的打印任务会被一直推迟,导致饥饿。活锁就是在忙式等待条件下发生的饥饿,忙式等待就是不进入等待状态的等待。
生产者消费者问题
两个进程共享一个公共固定大小缓冲区,生产者将消息放入缓冲区,消费者从缓冲区中取消息,当缓冲区已满,应先让生产者wait,等待消费者取消息后再notify生产者 反之同理
并发编程三大问题
原子性
可见性
有序性
要想解决并发造成的问题,就必须保证这三点
进程的调度算法
先到先服务
短作业优先
时间片轮转
优先级调度
多级反馈队列
短作业优先+优先级
进程间通信方式有哪些
匿名管道
有名管道
信号
信号量
消息队列
共享内存
套接字
线程间同步的方式有哪些
互斥量
mutex
信号量
semphares
事件
wait
notify
线程或进程的状态
程序员可以通过代码控制线程处于初始、就绪、阻塞 但不能控制线程在运行状态,什么时候执行run方法是由cpu调度决定的,程序员决定不了
初始状态new
Thread t=new MyThread();
线程创建方式
1.Runnable
2.Callable?????
就绪状态Runnable
t.start()
运行状态Running
在一个子线程运行状态中,有些代码必须是要合在一起要么执行失败要么全部执行成功。 如果在该子线程运行到一半,cpu调度给另一个子线程,则当前子线程进入阻塞(就绪),另一个子线程进入运行状态,就会出错。 抢票bug
由CPU调度
阻塞状态blocked
等待阻塞
wait()
wait和join的区别是, join的目的是为了让调用join的线程跑完/跑xx毫秒
同步阻塞
一旦一个线程获取锁进入同步块,在其出来之前,如果其它线程想进入,就会因为获取不到锁而阻塞在同步块之外,这时的状态就是 BLOCKED。
synchronized
其他阻塞
sleep() join()
死亡状态terminated
interrupt可以让线程优雅的死亡 join让线程执行完也可以死亡
run方法执行完
线程与进程的区别
进程与进程之间不共享内存
线程与线程之间共用他们所属的进程的内存资源
线程分类
Java线程分类
用户线程和守护线程的区别是java虚拟机是否存活 用户线程:当任何一个用户线程未结束,Java虚拟机是不会结束的。 守护线程:只剩守护线程未结束,Java虚拟机结束。没有用户线程了,守护线程就没有存在的意义了
用户线程
平时使用的线程都是用户线程
Activity
类似于活动和服务的关系,活动死了服务也会死,用户线程的环境(比如main)挂了,用户线程也会跟着挂 只要程序中有一个用户线程在运行,那么JVM就不会停止
后台线程
又叫守护线程、精灵线程 如果用户线程没了,后台线程就会自己自杀 用于服务用户线程的就是守护线程
Service
垃圾回收线程
Linux线程分类
用户线程
内核线程
监控应用的线程数
图形化Java监控工具
Java VisualVM
Java API
Thread.activeCount()
命令行工具
top -H -p 1
线程池
概念
分类 Java提供的7种线程池
FixedThreadPool
创建一个固定大小的线程池
CachedThreadPool
创建一个可缓存的线程池
SingleThreadExecutor
创建单个线程数的线程池
ScheduledThreadPool
创建一个可以执行延迟任务的线程池
原理
为什么要这样设置线程?
应用
手写线程池
线程池中的线程怎么设置?
创建线程池
Executors.newxxx(线程池类型的名字)
创建线程池 可规避资源耗尽风险
ThreadPoolExecutor
1.它的构造方法
2.通过Executor框架的工具类Executors来实现
线程池执行任务
线程池对象.execute()
用于提交不需要返回值的任务
线程池对象.submit()
用于提交需要返回值的任务,可通过返回的Future对象的get()方法获取返回值
子主题 1
使用线程池的例子
子主题 1
Runnable worker=new MyRunnable();
new线程
executor.execute(worker)
线程池.执行(线程)
线程相关的关键字
ThreadLocal
https://mp.weixin.qq.com/s/gJwVVX2CEiy_3XQ2PokAtw
线程安全
volatile
变量同步
synchronized
synchronized(this) /synchronized("常量") 括号里面,只要放对象都可以,但对于多个线程,要想把他们都锁住,就得要用一个线程公有的对象,比如this当前对象,或者继承线程类后,new一个属性对象,用这个属性对象来作为锁 使用线程通信三个方法:wait notify notifyAll时,一定要在方法或代码块加synchronized
代码块
方法
线程的调度算法
在代码调试过程中,调试所观察的线程是随机的,如果有两个子线程分别使用着这块run代码,则观察时,可能上一步i==10,下一步i就等于5,还没i--呢 因为另一个线程拿到了更多的cpu 抢到不意味着就要执行完。其他线程仍有机会再次抢回来。但如果在抢到cpu的时间内,该线程已经执行完,那就另当别论
时间片轮转
分时调度、平均分配每个线程占用CPU的时间
优先级调度
抢占型调度 按照优先级分配CPU时间
线程通信
背景:A类需要调用B类的对象,B类需要调用A类的属性
1.构造方法
做法:A类new一个B对象,同时在new构造方法中将A类的属性传进去,B类声明属性,在构造方法中接收
2.静态属性
临界资源
线程的创建方式
继承Thread类
写一个类,继承Thread类 重写run方法 新建状态: 实例化该类 一旦继承了Thread类,由于java单继承 就不能再继承其他的类
自定义一个线程类
实现Runnable接口
写一个类,实现Runnable接口 重写run方法 新建状态: 实例化对象 将实例化对象作为Thread的构造方法的参数 java支持多实现 所以推荐用这个办法 注意,如果你只new一个Runnable对象,却用这个对象作为多个实例化Thread对象的参数。则所有的Thread对象将会共享该Runnable对象的堆内存
实现该接口的类不是线程类,只是线程执行时必要的条件
实现Callable接口
两者区别:Runnable接口不会返回结果或抛出异常,但Callable可以,如果不需要返回结果或抛出异常就使用Runnable使得代码简洁
Future创建线程
线程Thread类的属性/方法
ThreadLoacl局部变量
概念
ThreadLoacl不是一个Thread,而是Thread类的局部变量
概要
作用
解决线程安全,它为每个线程提供一个独立的变量副本,ThreadLoacl比synchronized解决线程安全问题更简单
概要
应用场景
int a 把变量a放到ThreadLoacl类型对象中,使变量在每个线程都有独立拷贝,不会出现一个线程读取变量而被另一个线程修改的现象
还是不明白到底怎么用的
Thread类实例化对象可调用
setName()
getName()
currentThread()
setPriority()
setDaemon(true)
将线程标记为后台线程
join()
join应用于想要把子线程执行完的情况 将该线程加入到当前执行的线程中,控制当前环境的线程比如main,进入等待,只有当该线程run执行完以后,main下面的语句才能继续执行 main{ t1.start() t1.join() sout //等着上面执行完才能执行这句 sout }
interrupt()
中断 应用于打断sleep,打断sleep的方法只有中断 原理是每次写sleep都要加try catch 而interrupt就是抛出异常,让代码进入到catch块,从而能继续执行下去
isInterrupted
true 有中断信号 false 无中断信号 应用于while循环的条件
yield()
礼让 该线程抢到资源后,要把cpu让给其他线程
Thread静态方法
Thread.slepp
Object类继承而来
这三个方法在使用时,如果所属的方法或代码块没有加synchronized,就会报异常
wait
等待 释放cpu,等待被notify
notify
通知 通知一个已经wait的对象
notifyAll
都用于线程通信
线程操作
命名和取得名字
休眠
Thread.sleep
中断
thread.interrupt()
礼让
thread.yield()
优先级
thread.setPriority(低中高)
停止
老方法 容易造成死锁
stop
suspend
resume
对线程的操作添加while标志位 如果标志位为false则线程就停止运行
写一个stopRun,里面对标志位进行修改
System.exit(0)
用于子线程控制主线程死亡
Runnable
blocked
timed_waiting
终止terminated
新建new
就绪ready
阻塞waiting
运行running
JVM层面
waiting
OS层面
网络编程
TCP
应用
服务端
1.ServerSocket new一个出来
2.accept() 接收Socket对象
输入流读客户端信息
输出流写信息
关闭流
客户端
1.Socket new一个出来
输出流写信息
输入流读服务端的客户端信息
关闭流
类与方法
ServerSocket
Socket
UDP
应用
发送方
构建发送方的数据报端口 可以不指定ip
发数据报包
创建数据报包
发送
收数据报包
构建空包用于接收数据报包
拆包
显示数据
关闭
接收方
构建接收方的数据报端口 一定要指定ip port
接收数据报包
构建空包用于接收数据报包
拆包
显示数据
发送数据报包
创建数据报包
发送
关闭
类与方法
InetAddress
DatagramPacket
DatagramSocket
封装UDP
广播
数据库
数据库索引
分类
索引有哪些???
b+树和b树的区别是什么???
数据结构??
索引失效
存储引擎
分类??
InnoDB和Mylsam的区别??
SQL语法
每个SQL语句都是由一个或多个关键字构成
数据库概念
DBMS 数据库软件
一个数据库软件可以创建多个数据库
database 数据库
文件柜
table 表
一个个文件
列
表中的一个字段
学生名字 学生学历
行
一行表示一个记录
张三 本科
主键
标识一个记录的唯一标识
学号
SQL
Structure query language结构化查询语言
MySQL
概念
SQLite和mysql的区别???
原理
应用
B+树的深度怎么计算??
常用范式
子主题 1
三大范式???
数据库建表需要注意什么??
数据库索引使用B+树,为什么不用红黑树?
红黑树的结构
数据库隔离级别??
可重复读是解决什么问题?
数据库常用指令
模糊查询
like
数据库事务四大特性
原子性
持久性
一致性
隔离性
面试
你要有一块非常精通的知识点,比如线程相关,然后不论什么样的面试,你都把这些问题引入到你深挖的地方
项目经历
我的准备
新闻APP
项目的背景介绍
APP的界面展示
APP的数据流向
比赛获奖的项目给我讲一下你都做了什么
实习经历
场景设计
计算机基础 我的套路准备
线程和进程的区别
通过线程引出线程通信方式IPC
操作系统的线程通信分为Linux的线程通信和Android的线程通信
Linux线程间通信方式有
管道
共享内存
消息队列
引出安卓Binder
Binder的底层是AIDL
AIDL的使用
Android的有
子主题 1
操作系统
计算机基础 面试官
操作系统
计算机网络
计算机组成原理
cpu
数据库
Linux命令
提前在面试14天前开始准备面经
设计模式
创建型模式
合成
工厂
手写??
单例
概念
私有化构造方法,私有化类中唯一一个类对象
应用
懒汉模式
它很懒,只有在你需要它时才初始化 ,而不是在类加载期间就进行加载,但获取对象会比较慢
线程不安全
私有静态类对象
private static instance;
私有构造方法
private Singleton(){ }
公共静态get类对象方法
public static getInstance() { 判空,为空就实例化 再返回instance }
synchronized的线程安全
public synchronized static getInstance() { 判空,为空就实例化 再返回instance }
双重锁校验的线程安全
双重锁是对 上一个synchronized方法锁的优化,减少部分获取实例的耗时(方法锁比变量锁耗时 为什么??) 同时也满足懒加载
public synchronized static getInstance() { if(null!= instance ) synchronized(类名.class) if(null!=instance) }
饿汉模式
它很饿,所以在类加载期间就进行加载,比较勤劳 直接导致消耗内存 但获取对象比较快
线程安全
使用类的内部类 线程安全
CAS线程安全
手写??
多例
结构型模式
代理
张三是测试app 成人用品加工厂是真实业务实现部分 淘宝是代理业务部分 对于测试app,只能看到淘宝也就是代理业务,而真正的实现部分是成人用品加工厂 添加一个代理业务部分,还可以增加额外的新业务,如在原有的产品基础上淘宝还可以支持售后,送红包 类似TCP中客户端与服务端交互,其实服务端也会弄出一个代理的客户端让外部客户端与内部客户端进行通信
静态代理模式
违反开闭原则 开闭原则就是 对修改关闭 对新增开放 我是测试app 淘宝成人用品店是代理 制造厂是真实业务 如果换一个人比如 我老婆是测试app 她想买吃的 那我就得在淘宝成人用品店里添加新的业务 ,添加新的食品厂这个接口 就违反了“对修改关闭”
应用
真实业务实现部分
代理业务部分
需要一个代理功能就写一个代理类,100个代理功能就要写100个代理类
真实业务接口
测试app
手写一个静态代理??
动态代理模式
动态代理一定比静态代理的效率要低 弥补了静态代理中,代理业务部分经常要修改的问题 我/我老婆是测试app 淘宝 包括成人用品、食品 是代理 制造厂、食品厂是真实
概念
静态代理模式实现了我们通过票贩子买票的功能,但票贩子这个代理类需要我们手动编码定义,如果需要代理的对象多了,比如有一百多个需要代理的功能(代理进站、代理托运、代理换乘...等等),那岂不是要建一百多个代理类,这个时候就需要我们的动态代理模式了,动态代理与静态代理最大的区别是,在运行时让虚拟机帮我们生成一个对应的代理类,来调用对应的方法,并且在使用结束后回收,解决了静态代理的局限性。
应用
真实业务实现部分
代理业务部分
应用Proxy类获取动态代理对象
再通过动态代理对象对真实业务实现进行增强和扩展
真实业务接口
测试app
手写一个动态代理???
行为型模式
观察者
手写????
适配器模式
概念
把一个类的接口变换成客户端所期待的另一种接口
解决了什么问题?
适配问题
应用
字节流转字符流 用InputStreamReader
装饰器模式
概念
动态地往一个类添加新的行为的设计模式 ,比写新的子类要灵活
解决了什么问题?
我们一般为了扩展类的功能要经常继承,而且功能越多,子类越多
应用
创建一个AnimalDecorator类,对一个Animal接口进行装饰,
再对AnimalDecorator类进行继承,新增更多的子类
不是设计模式
类与类之间的通信,数据交换
背景:A类需要调用B类的对象,B类需要调用A类的属性
做法:A类new一个B对象,同时在new构造方法中将A类的属性传进去,B类声明属性,在构造方法中接收
属性不能被自己所在的类的其他方法所调用
把属性放在方法外面进行声明,在方法内部进行new
七大设计原则
问题
适配器模式??
装饰器模式??
C++
面向对象
继承
多态
虚函数
纯虚函数
抽象类
全纯虚函数
接口
语法基础
关键字
const
static
&
*
回调
高级
泛型
STL(标准模板库)