码出高效 Java开发手册
2022-03-22 09:30:36 0 举报AI智能生成
码出高效Java开发手册-阿里巴巴,知识脉络以及读书笔记,方便回顾知识点
Java
码出高效
Java开发手册
JVM
模版推荐
作者其他创作
大纲/内容
计算机基础
0/1的世界<br>
原码<br>
如果机器字长为n,那么一个数的原码就是用一个n位的二进制数,<br>其中最高位为符号位:正数为0,负数为1。<br>剩下的n-1位表示该数的绝对值。
反码
正数的反码是其本身<br>负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
补码
正数的补码是其本身<br>复数补码也非常的简单,就是在反码的基础上按照正常的加法运算加1<br>
位运算
左移<< 右移>><br>运算中 , 符号位均参与移动 , 除负数往右移动 , 高位补 l 之外,其他情况均在空位处补 0 ,
三个大于号的>>> 无符号向右移动<br>忽略符号 强制向右移动 高位补0
按位取反 ~
按位与 &
按位或 |
按位异或 ^<br>
浮点数
单精度
4字节
双精度
8字节
字符集与乱码
字符集
ASCii 码表
7 组连续的信号量 + 1个校验位 组成ASCii 码表 127位
10 和 13 位 是 \n \r 表示换行 <br>在不同操作系统中 使用的不一样 Windows '\r\n' unix '\n'<br>
中文
GB2312 只存储6000多字
GBK GB基础进行扩张
UTF-*
CPU 与内存
CPU
控制器
控制单元、指令译码器、指令寄存器
控制单元是 CPU 的大脑,由时序控制和指令控制等组成
指令译码器是在控制单元的协调下完成指令读取、分析并交由运算器执行等操作
指令寄存器是存储指令集,当前流行的指令集包括 X86 、 SSE, MMX 等
运算器
算术逻辑运算单元,即 ALU<br>能够执行算术运算或逻辑运算等各种命令,运算单元会从寄存器中提取或存储数据<br>
寄存器<br>
高速缓存
内存
运行程序的空间、与CPU直接进行沟通
TCP/IP
网络协议
中文: 传输控制协议/因特网互联协议<br>包含了HTTP, HTT陀、 FTP 、SMTP 、 UDP, ARP 、 PPP 、 IEEE 802.x
协议分层
应用层:HTTP/FTP/SMTP<br>传输层:TCP/UDP<br>网络层:IP<br>链路层:PPP
链路层
单个 0、 l 是没有意义的,链路层以字节为单位把 0 与 l 进行分组,<br>定义数据帧,写入源和目标机器的物理地址、数据、校验位来传输数据
网络层
根据 IP 定义网络地址,区分网段
IP协议
传输层
数据包通过网络层发送到目标计算机后,应用程序在传输层定义逻<br>辑端口 , 确认身份后,将数据包交给应用程序,实现端口到端口间通信
应用层
传输层的数据到达应用程序时,以某种统一规定的协议格式解读数据。
IP协议
网络层
IP 地址属于网络层,主要功能在 WLAN 内进行路由寻址,选择最佳路由。 IP 报<br>文格式 共 32 位 4 个字节,通常用十进制数来表示。四地址的掩码<br>Ox值班。。表示 255.255.255.0 ,掩码相同,则在同 一子网内。 IP 协议在 IP 报头中记录<br>源 IP 地址和目标 IP 地址
IP报文格式
生存时间TTL,每经过一个路由器TTL -1 到0后就废弃<br>
TCP建立链接
传输控制协议,是 种面向连接、确保数据在端到端间可靠传输的协议<br>
报文格式
第一行端口号与上层IP来确定通信,开启监听<br>客户端发送 SYN=1 , 服务端接受 ACK = 1 ,客户端发送 SEQ = y ack+1 这样用来保证可靠性<br>
从编程的角度, TCP 连接的建立是通过文件描述待( File Descriptor ,“) 完成的。<br>通过创建套接字获得一个 fd , 然后服务端和客户端需要基于所获得的 “ 调用不同<br>的函数分别进入监昕状态和发起连接请求。由于臼的数量将决定服务端进程所能建<br>立连接的数量 ,对于大规模分布式服务来说,当 阳 不足时就会出现 、pen too many<br>files ” 错误而使得无法建立更多的连接。为此,需要注意调整服务端进程和操作系统<br>所支持的最大文件句柄数。通过使用 ulimit -n 命令来查看单个进程可以打开文件句柄<br>的数量。
断开链接
四次挥手
客户端发送 TCP报文FIN字段设置为1 seq = u
服务端 发送 ack = 1 seq = u+1 <br>
服务端发送 FIN = 1 <br>
客户端回应ACK
客户端发送断开之后、服务器ACK 在此之后的一段时间称为 close_wate<br>
服务器发出断开后 客户端响应 , 会有一线段时间 成为 time_wait 等待断开<br>
可以在服务器上修改配置 telsysctl.conf
信息安全
XSS
跨站脚本攻击
页面使用脚本获取后台数据
解决:Jsoup
CSRF<br>
跨站请求伪造
同一浏览器可以获取用户token,会造成恶意进攻
解决<br>1:CSRF Token 验证,利用浏览器的同源限制,在 HTTP 接口执行前验证页面<br>或者 Cookie 中设置的 Token ,只有验证通过才继续执行请求。<br>2:人机交互 ,比如在调用上述网上银行转账接口时校验短信验证码。<br>
HTTPS
HTTP+SSL(安全套接字层)
SSL 协议工作于传输层与应用层之间,为应用提供数<br>据的加密传输,可以理解接受到HTTP报文之后进行SSL加解密这个操作<br>
非对称加密
将密码分为公钥和私钥<br>私钥是用来对公钥加密的信息、进行解密的,是需要严格保密的。<br>公钥是对信息进行加密,任何人都可以知道,包括黑客。<br>
CA机构
权威认证的HTTPS证书
访问过程
1:浏览器向服务器发送请求,请求中包括浏览器支持的协议,并附带一个随机数。<br>2:服务器收到请求后,选择某种非对称加密算法,把数字证书签有公钥、身份信息发送给浏览器,同时也附带一个随机数。<br>3:浏览器收到后、验证证书的真实性,用服务器的公钥发送握手信息给服务器。<br>4: 服务器解密后 , 使用主前的随机数计算出,对称加密的密钥 , 以此作为加密信息并发送。<br>5: 后续所有的信息发送都是以对称加密方式进行的。
面向对象
面向对象思维
以对象模型为核心 , <br>丰富模型的内涵,扩展模型的外延,通过模型的行为组合去共同解决某一类问题,<br>抽象能力显得尤为重要 , <br>封装是一种对象功能内聚的表现形式,使模块之间辑合度变低,更具有维护性,<br>继承使子类能够继承父类,获得父类的部分属性和行为,使模块更有复用性 ; <br>多态使模块在复用性基础上更加有扩展性,使运行期更有想象空间。
经典Object类
getClass()
我是谁 toString() 相当于名片书写
Object()
我从哪里来
finalize()
对象的消亡
clone()
浅拷贝
只复制当前对象的所有基本数据类型,以及相应的引用变量,但没有复制引用变量指向的实际对象
一般深拷贝
浅拷贝和彻底深拷贝之间
彻底深拷贝
成功 clone 个对象之后,此对象与母对象在任何引用路径上都不存在共享的实例对象 ,但是引用路径递归越深,越接近 JVM底层对象
clone 默认浅拷贝,要使用深拷贝需要重写方法
hashCode()/equals()
每个类独有的、世界因你而不同
wait()/notify()
线程之间的协作
经典特性
继承
封装
多态
override -- 重写<br>
子类实现接口 , 或者继承父类时 , 保持方法签名完全相同,实现不同的方法体 , 是垂直方向上行为的不同实现<br>
overload -- 重载
方法名称是相同的,但是参数类型或参数个数是不相同的,是水平方向上行为的不同实现
多态是指在编译层面无法确定最终调用的方法体 , 以覆写为基础来实现面向对象特性,<br>在运行期由 JVM 进行动态绑定 , 调用合适的重写方法体来执行<br>
初识Java
接口与抽象类
abstract - interface
抽象类:对同类事物具体的抽象、属于模板式设计<br>
接口实现类要有能力去实现并执行接口 中定义的行为、属于契约式设计<br>
内部类
任何一个类都可以在内部定义另外一个类,内部类作为类的一个属性
静态内部类
static class StaticinnerClass {} ;
成员内部类
private class InstancelnnerClass {} ;
局部内部类
方法或者表达式内部<br>
匿名内部类
(new Thread(){} ).start()
控制访问权限
this\super
this() 子类构造方法 super() 父类构造方法
类内方法
实例方法
静态方法
静态代码块
重写
override<br>
如果父类定义的方法达不到子类的期望,那么子类可以重新实现方法覆盖父类的实现
返回值能够向上转化为父类的返回值
异常向上抛出
方法名和参数必须一致
非静态、非final、非构造
代码风格
命名体现代码元素
一般为动词,与参数组成动宾结构
FileInputStream
命名最好望文知义
常量
作用域内保持不变的值、用public static final修饰
全局:采用全部大写
public static final String GLOBAL CONSTANT = ” shared in global ” ;
局部:采用小写
final String methodConstant = ” shared in method ” ;
禁止使用魔法值,应该在外部定义
变量
小驼峰格式
代码展示与风格
缩进:Tab
空格
任何二目、三目运算符左边都需要加一个空格
注释双斜线与内容应该有一个空格
方法参数入参是,逗号后面都应该有一个空格
没必要增加空格与上一行对齐
大括号中间无需加空格<br>
左右小括号与内部内容无需加空格
左大括号前加空格
空行
方法定义之后、属性定义与方法之间、不同逻辑、不同语义、不同业务的代码之间都需要通过空行来分隔。
换行
第二行与第二行缩进4个空格
运算符与下文一起换行
在括号前不要换行
方法调用中的多个参数需要换行时 , 在逗号后换行。
方法行数最好保持80行
空制语句
if else for while 后必须加大括号<br>
条件表达式中不允许有赋值操作
循环嵌套不循序操作3层
避免采用取反运算逻辑
走进JVM
字节码
0\1通过不同的组合产生不同的字符、产生不同的机器指令,环境不同、指令不同,在代码和计算机之间有一个中间层,就是字节码 ByteCode
JVM ->编译执行字节码
.class 文件 ,打开是二进制文件,可转化为16进制后<br>每个16进制表示二进制流,代表一个操作指令<br>
cafe baby 代表Java文件
0X37代表JDK11版本
JVM中也有助记符
ICONST_0 - 0000 0011 - Ox03,<br>
ALOAD_0 - 0010 1010 - OX2a<br>
POP - 0101 0111 - Ox57
A代表引用类型操作、I代表整数类型操作 其他类型均是其类型的首字母<br>
字节码主要指令
加载或存储指令
将局部变量加载到操作栈中(ILOAD、ALOAD)
从操作栈顶存储到局部变量表中(ISTORE、 ASTORE)<br>
将常量加载到操作栈顶、极为高频 (ICONST、 BIPUSH 、 SIPUSH 、 LDC)<br>
ICONST 加载的是 1 ~ 5 的数(!CONST 与 BIPUSH 的加载界限)
BIPUSH ,即 Byte Immediate PUSH ,加载 -128 ~ 127 之间的数
SIPUSH ,即 Short Immediate PUSH ,加载 -32768 ~ 32767 之间的数
LDC ,即 Load Constant ,在 -2147483648 ~ 2147483647 或者是字符串时,<br>JVM采用 LDC 指令压入枝中。
指令运算
对两个操作栈帧上的值进行运算,并把结果写入操作栈顶,如 IADD 、 IMUL 等。
类型转换指令
显式转换两种不同的数值类型。如 I2L 、 D2F 等
对象创建与访问 指令
创建对象
NEW 、 NEWARRAY
访问属性
GETFIELD 、 PUTFIELD 、 GETSTATIC
检查实例类型指令
INSTANCEOF, CHECKCAST
操作栈管理指令
出栈
POP POP2
复制栈顶元素并且入栈
DUP
方法调用与返回
INVOKEYIRTUAL 指令 调用对象的实例方法。
INVOKESPECIAL 指令 调用实例初始化方法、私有方法、父类方法等。
INVOKESTATIC 指令 调用类静态方法。
RETURN 返回VOID类型
同步指令
NM 使用方法结构中的 ACC_SYNCHRONIZED 标志同步方法 , 指令集中有<br>MONITORENTER 和 MONJTOREXIT 支持 synchronized 语义。
其他
LINENUMBER 存储了字节码与源码行号的对应关系 方便调试
LOCALVARIABLE 存储当前方法中使用到的局部变量表。
Java文件编译成字节码的编译过程
Java文件 -- 词法解析 -- 语法解析 -- 语义分析 -- 生成字节码<br>
词法解析
通过空格分隔 出单词 、 操作符、控制符等信息 , 将其形成 token 信息流 ,传递给语法解析器
语法解析
把词法解析得到的 token 信息流按照 Java 语法规则组装成一棵语法树
语义分析
检查关键字使用是否合理、类型是否匹配、作用域是否正确,语义分析完成后可生成字节码
字节码加载
解释执行
JIT编译执行<br>
两者混合<br>
解释器在启动时先解释执行,省去编译时间。JVM 通过热点代码统计分析 , 识别高频的方法调用、循环体、公共模块等,<br>基于强大的 JIT 动态编译技术,将热点代码转换成机器码,直接交给 CPU执行。<br>
类加载过程
ClassLoader 将class文件加载到内存中、双亲委派模型<br>
类加载器
运行时核心基础设施模块
加载<br>
读取类文件产生的二进制字节流并转化为特定的数据格式<br>初步校验 cafe baby魔法值、常量、文件长度、父类,然后创建对应的 java.lang.Class 类型<br>
链接
验证、准备、解析 三个步骤<br>
验证:更为详细的校验,final修饰是否正确,类型引用是否正确、静态变量是否合理
准备: 为静态变量分配内存、设置默认值<br>
解析:类和对象之间的引用正确性<br>
初始化
执行类构造器clinit方法
如果其中赋值依赖其他类、会立刻解析其他类<br>
加载顺序<br>
Bootstrap 类加载器:由JVM启动时候创建、负责装载最核心的类 Object 、 System 、 String 等;<br>
由C++ 实现
JDK9:platform 类加载器:平台加载器、加载加密、压缩、XML解析等方法<br>
JDK9之前:Extension ClassLoader 类加载器 功能与platform 相似
Application ClassLoader : 加载用户定义的class路径下的类
用户也可以自定义classLoader
类加载器具有等级制度、但非继承关系,是以组合的关系来进行复用<br>低层次的不能覆盖高层次的,故有了双亲委派模型
这个类加载了吗? -- 向上询问,直至BootStrap<br>我是否可以下载这个类? -- Bootstrap向下询问,尝试可加载<br>
JVM 内存结构
简图
Heap堆
OOM 故障发源地、存储着几乎所有对象实例、垃圾收集器自动回收<br>
-Xms256M Xmx512M 最小堆 - 最大堆<br>-X表示JVM参数 ms表示内存memory start 初始 mx表示memory max 最大<br>建议两者调成一致的,因为堆内存存在压缩与释放,如果发生GC,频发压缩、释放会对计算机产生压力 <br>
分为新生代、老年代,对象创建在新生代、到了暮年后转为老年代
新生代
伊甸园、幸存者 1:2<br>新创建对象在伊甸园区,<br>伊甸园填满后会触发YGC,没有引用的对象进行清除,存活的对象放到幸存者区<br>
Survivor 幸存者区域: 分为两个 S0\S1,GC采用赋值算法,每次会复制到未使用的区域
每个对象都有一个计数器 发成YGC后会进行+1,直到 -XX:MaxTenuringThreshold 会放到老年区<br>
老年代
接受新生代存不下的对象、
老年代也放不下会触发FGC、如果依旧放不下会触发OOM <br>-XX:+HeapDumpOnOutOfMemoryError ,让JVM 遇到 OOM 异常时能输出堆内信息,特别是对相隔数月才出现的 OOM 异常尤为重要。<br>
Metaspace 元数据
在本地内存分配、类元信息、字段、静态属性、方法、常量
JVM Stack 虚拟机栈<br>
方法开始时,只有栈顶的栈帧时有效的<br>正在执行的方法称为当前方法 , 栈帧是方法运行的基本结构。<br>正常运行:结束后指向下一个栈帧<br>异常报错:进行回溯,返回地址通过异常处理表确定<br>
栈帧
局部变量表
是存放局部变量和方法参数的区域<br>字节码指令中的 STORE 指令就是将操作栈中计算完成的局部变量写回局部变量表的存储空间内。<br>
操作栈
方法执行过程中,对具体运算进行压栈和出栈,来进行数据运算<br>
例如: 数据压栈BIPUSH 、数据加载 ILOAD<br>
i++ 和 ++i 的区别
i++ 先进行数据加载,然后进行运算
++i 先运算字节码,然后进行结果赋值
两者汇编指令不一样
动态连接
每一个栈帧对应的是一个常量池的引用,目的是支持对应方法调用过程的连接<br>
返回地址
退出
正常返回字节码状态 return ireturn 等
错误、异常退出
退出三种方式
返回值压入上层调用栈帧
异常信息抛给能够处理的栈帧
PC 计数器指向方法调用后的下一条指令
Native Method Stacks (本地方法栈 )
本地方法栈为 Native 方法服务、调用系统方法
Program Counter Register (程序计数寄存器)
由于CPU分片运行,PC计数器就会记录当前执行的状态,以供下次继续运行
对象实例化
Object o = new Object() <br>javap -verbose -p *.class 文件进行分析<br>
字节码角度
stack=2, locals=2, args_size=1<br> 0: new #2 // class java/lang/Object<br> 3: dup<br> 4: invokespecial #1 // Method java/lang/Object."<init>":()V<br> 7: astore_1<br> 8: return
New
找不到对象,开启类加载机制,加载成功后开启,在堆中分配内存
分配完成内存后,将实例对象压入虚拟机栈顶<br>
dup
将栈顶赋值到引用变量,<br>此时两个指针指向对象,一个进行赋默认值,另一个设置为句柄进行方法调用<br>
invokespecial
调用对象实例方法,通过栈顶的引用变量调用<init>方法
对象创建角度
确认类无信息是否存在,存在即引用,不存在及加载字节码
分配对象内存
<span class="fontstyle0">设定默认值。</span><br style="font-variant-numeric: normal; font-variant-east-asian: normal; line-height: normal; text-align: -webkit-auto; text-size-adjust: auto;">
设置对象头
执行 init 方法
垃圾回收
相关概念
存活对象: 有引用的对象<br>
STW:垃圾回收期间,整体JVM停止工作,程序卡顿
垃圾回收算法
标记-清除
将有引用的对象进行标记,那么没被标记的就会清除
标记- 拷贝
将内存分为1:1,将有引用的进行标记,然后复制到另一块区域,然后清除该区域
标记-压缩
标记存活的对象,然后将存活对象整理到内存空间的一端,形成连续的已使用空间 , 最后把已使用空间之<br>外的部分全部清理掉
三色标记
CMS
产生STW
SATB
对象引用、回收
垃圾回收器
Serial
YGC 清除方式、单线程的方式
标记-整理 标记-拷贝 两种方式
CMS
多线程方式进行处理
并发的进行 标记-清除
产生游离对象
G1内存分区
内存划分区域,分为四类Eden 、Survivor 、 Old 、 Humongous(Old中的大对象)<br>优先回收垃圾最多的区域
标记-拷贝
三色标记
SATB
调优
DUMP 文件分析
工作遇到Java heap space 报错、进行分析
0:查看内存当前线程 ps -aux|grep serviceName
1:生成JVM 运行时快照DUMP <br>jmap -dump:live,format=b,file=heap-dump.bin <pid><br>
#出现 OOME 时生成堆 dump: <br>-XX:+HeapDumpOnOutOfMemoryError<br>#生成堆文件地址:<br>-XX:HeapDumpPath=/opt/jvmlogs/
2:DUMP 文件分析
使用Jprofile 分析dump 文件
可以看到那些数据结构占用空间大小,进行数据分析
数据结构与集合
数据结构
线性结构
树形结构
图形结构<br>
hash结构
时间复杂度
常数级 0(1 )、对数级 O(logn)、线性级 0(n)、线性对数级<br>O(nlogn ) 、平方级 O(n^2)、立方级 O(n^3)、指数级 0(2^n)<br>
集合框架图
类图:红色代表接口 , 蓝色代表抽象类 , 绿色代表并发包中的类,灰色代表早期线程安全的类
主要分为两大类
第一类是按照单个元素存储的 Collection(set、List、Queue、Map实现)
第二类是按照 Key-Value 存储的 Map
List集合
ArrayList
LinkedList
Queue集合
Queue (队列)是一种先进先出的数据结构,队列是一种特殊的线性表,它只允<br>许在表的 端进行获取操作,在表的另一端进行插入操作。当队列中没有元素时,称<br>为空队列。自从 BlockingQueue (阻塞队列)问世以来,队列的地位得到极大的提升,<br>在各种高并发编程场景中,由于其本身 FIFO 的特性和阻塞操作的特点,经常被作为<br>Buffer (数据缓冲区)使用。
Map集合
key-value
Set集合
不允许出现重复元素的集合类型
hashSet
TreeSet
LinkedHashSet
集合初始化<br>
ArrayList
1:初始化时候,传入多大的参数就生成多大的Object[] 数组
2:扩容<br>新的容量 int newCapacity = oldCapacity + (oldCapacity >> 1); 位运算右移一位会产生另外一个正数,<br>超过最大位就比他小,因此后面进行了判断,如果new小于old,那么进行size + 1 操作<br>
3:如果长度位1000,那么初始化需要扩容13次才可以完成,如果直接初始化1000,就不会扩容,进行多余计算,如果不注意内存分配,产生OOM问题<br>
HashMap
HashMap不会再new的时候创建内存,而是在第一次put时候进行创建初始大小
两个重要参数,Capacity 和 Load Factor
capacity 容量大小
LoadFactor 负载因子 , *capacity 与实际存储进行对比,判断是否需要扩容 <br>
数组与集合
Arrays
Arrays.asList() 转换以后不能使用其 add/remove/clean 方法 否则会抛出UnsupportedOperationException 异常
集合与泛型
List、List<Object>、List<?>
List<?> 代表集合通配符、可以接受任何类型的集合引用和赋值,但是不能添加,只能remove和clear<br>一般作为参数来接收外部的集合,或者接收不知道返回类型的集合<br>
List<T> 、<? extends T>、 <? super T><br>
T只能放一种类型
<? extends T> 只能放T和子类
<? super T> 只能放 T和父类
元素比较
Comparable 和 Comparator
Comparable 是自己和自己进行比较时候会调用的方法,比较方法是compareTo
Comparator 是 自己和其他元素对比调用的方法,调用的方法是compare
约定俗成,不管是 Comparable 还是 Comparator , 小于的情况返回 -1 , 等于的情况返回 0 ,大于的情况返回 l 。
hashCode 和 equals
hashCode 和 equals 用来标识对象 , 两个方法协同工作可用来判断两个对象是否相等。
任何情况下 重写equals的同时必须重写hashCode
fail-fast 机制
Map 类集合
Map 类的特点
Map 类取代了旧的抽象类 Dictionary ,拥有更好的性能。<br>没有重复的 Key ,可以有多个重复的 Value。<br>Value 可以是 List 、 Map 、 Set 类对象。<br>KV 是否允许为 null ,以实现类约束为准。
提供了三个特殊方法 keySet()\values()\entrySet()<br>
了解到的Map集合
注意点:ConcurrentHashMap 的key和value都不允许为空
红黑树
树
平衡二叉树
每个节点最多有两个叶子
二叉查找树
左节点<根节点<右节点
容易失衡,导致深度过深
AVL树
二叉树基础上添加左旋、右旋操作,深度不会加深
红黑树
AVL树每次都要判断旋转、红黑树深度超过广度的二倍才会旋转
AVL是绝对的平衡、红黑树是相对平衡
HashMap<br>
存储概念
table 存储所有节点的数据的数组
slot 哈希槽,类似table[i] 这样的位置<br>
bucket 哈希桶,类似table[i]上存储元素形成的表或者数的集合<br>
图解
扩容
负载因子
当前数量>最大数量*负载因子 就会进行扩容
16 是默认容量大小 0.75 是默认负载因子
迁移与源码分析
后续总结博客中
<span class="fontstyle0">ConcurrentHashMap</span><br style="font-variant-numeric: normal; font-variant-east-asian: normal; line-height: normal; text-align: -webkit-auto; text-size-adjust: auto;">
线程安全,推荐使用,完全可以提点HashMap, 使用了 lock-free 技术减少锁的影响
JDK11的优化
取消分段锁,降低冲突概率
引入红黑树
使用更加优化的方式统计集合内部元素数量<br>
源码分析
后续会总结到博客中
ReadWriteLock
并发与多线程
线程安全
线程状态
NEW新建
Thread、Runnable 接口、Callable 接口
runnable 就绪<br>
调用start()后<br>
running 运行
run()执行
blocked 阻塞
同步阻塞
获取不到锁,无法运行
主动阻塞
sleep()、 join()
等待阻塞
wait()
dead 终止<br>
run()结束后
如何保证安全<br>
1:数据单线程可见
2:只读对象
3:线程安全类
4:同步与锁机制
线程同步类
并发集合类
线程管理类
锁相关类
锁(JUC)
Lock
继承关系图
ReentrantLock
AQS
CountDownLatch
Semaphore
CyclicBarrier
StampedLock
同步代码块
synchronized
在方法签名处加 synchronized 关键字
对对象、类进行同步
原则是锁的范围尽可能小
实现原理
JVM实现
JVM找到找到对应对象的 monitor
同步代码块中会使用 monitorenter 及 monitorexit 两个字节码指令获取和释放 monitor。<br>如果使用 monitorenter 进入时 monitor 为 0 ,表示该线程可以持有 monitor 后续代码,<br>并将 monitor 加 l ,如果当前线程已经持有了 monitor , 那么 monitor 继续加 l ;如果<br>monitor 非 0 , 其他线程就会进入阻塞状态。
偏向锁、轻量级锁、重量级锁、还提供自动的升级和降级机制
00\01\10\11
线程同步
volatile<br>保证线程可见性<br>防止指令重排序
happen before
时钟顺序先后
防止指令重排序
指令优化-指令重排序
计算机并不会根据代码顺序按部就班地执行捆关指令、将相关指令信息进行合并
<span class="fontstyle0">每个线程都有独占的内存区域 </span><span class="fontstyle0" style="color:rgb(80,80,80);">, </span><span class="fontstyle0">如操作枝、本地变量表等。线程本地内存保存7<br>号|用变量在堆</span><span class="fontstyle0" style="color:rgb(80,80,80);">内</span><span class="fontstyle0">存中的副本 </span><span class="fontstyle0" style="color:rgb(80,80,80);">, </span><span class="fontstyle0">线程对变量的所有操作都在本地内存区域中进行 </span><span class="fontstyle0" style="color:rgb(64,64,63);">, </span><span class="fontstyle0">执行<br>结束后再同步到堆内存中去。这里必然有一个时间差 </span><span class="fontstyle0" style="color:rgb(80,80,80);">, </span><span class="fontstyle0">在这个时间差内,该线程对副<br>本的操作 </span><span class="fontstyle0" style="color:rgb(80,80,80);">, </span><span class="fontstyle0">对于其他线程都是不可见的。</span><br style="font-variant-numeric: normal; font-variant-east-asian: normal; line-height: normal; text-align: -webkit-auto; text-size-adjust: auto;">
适用于一写多读的场景
只能保证线程可见,不能保证线程同步
信号量同步
CountDownLatch
<span class="fontstyle0">Semaphore</span><br style="font-variant-numeric: normal; font-variant-east-asian: normal; line-height: normal; text-align: -webkit-auto; text-size-adjust: auto;">
线程池
参数
int corePoolSize , <br>int maximumPoolSize, <br>long keepAliveTime , <br>TimeUnit time,<br>BlockingQueue<runnable> workQueue <br>ThreadFactory threadFactory,<br>RejectedExecutionHandler handler
corePoolSize 表示常驻核心线程数
如果等于 0 ,则任务执行完之后 ,没有任何请求进入时销毁线程池的线程,如果大于 0 ,即使本地任务执行完毕,核心线程也不会被销毁。
maximumPoolSize 表示线程池能够容纳同时执行的最大线程数
如果待执行线程数大于最大线程数,就会放缓存队列中
keepAliveTime 表示线程池中的线程空闲时间
当空闲时间达到 keepAliveTime 值时,线程会被销毁,直到只剩下 corePoolSize 个线程为止,避免浪费内存和旬柄资源。
TimeUnit 表示时间单位
workQueue 表示缓存队列
当请求的线程数大于 maximumPoolSize时 , 线程进入 BlockingQueue 阻塞队列。
<span class="fontstyle0">threadFactory </span><span class="fontstyle0" style="color:rgb(106,106,106);">表示线程工厂</span><br style="font-variant-numeric: normal; font-variant-east-asian: normal; line-height: normal; text-align: -webkit-auto; text-size-adjust: auto;">
它用来生产一组相同任务的结程。线程池的命名是通过给这个 factory 增加组名前缀来实现的。
<span class="fontstyle0">handler </span><span class="fontstyle0" style="color:rgb(106,106,106);">表示执行拒绝策的对象</span><br>
保存到数据库进行 削峰填谷。在空间时提取出来执行。
转向某个提示页由。
打印日志
创建
Executors.newWorkStealingPool()
创建持有足够线程的线程池支持给定的并行度 , 并通过使用多个队列减少竞争
Executors.newCachedThreadPool()<br>
maximumPoolSize 最大可以至 Integer.MAX_VALUE, 是高度可伸缩的线程池 ,容易造成OOM<br>
Executors.newScheduledThreadPool()
maximumPoolSize 最大可以至 Integer.MAX_VALUE, 是高度可伸缩的线程池 ,容易造成OOM
Executors.newSingleThreadExecutor()
创建个单线程的线程池,相当于单线程串行执行所有任务 , 保证接任务的提交顺序依次执行。
Executors.newFixedThreadPool()
输入的参数即是固定线程数,既是核心线程数也是最大线程数 , 不存在空闲线程, 所以 keepAliveTime 等于 O<br>使用的是LinkedBlockingQueue 等待队列,是无界队列,也会造成OOM问题<br>
另外,Executors 中默认的线程工厂和拒绝策路过于简单,通常对用户不够友好<br>
创建工厂
自定义工厂创建
拒绝策略
默认的四种拒绝策略
AbortPolicy 默认、丢弃任务并抛出RejectedExecutionException异常
DiscardPolicy,丢弃任务,不抛出异常
DiscardOldestPolicy 丢弃掉队列中最久的任务
CallerRunsPolicy 直接调用run方法,放弃线程池的方式
使用自定义拒绝策略,可以记录日志或者数据库或者队列中,等空闲时候再处理
源码
博客更新
ThreadLocal
引用类型
强
Object o = new Object(); GC可达,必不可能回收
软
OOM之前,垃圾回收器会在回收考虑范围内
弱
YGC 就会被回收
虚
被回收时提供系统通知
图解
重要方法
set、get、remove
使用完成的变量应该快速remove掉,避免内存泄漏
单元测试
JUnit5.x
三个模块
JUnit Platform
用于在 NM 上启动测试框架 , 统一命令行、 Gradle 和 Maven执行测试入口
<span class="fontstyle0">JUnit Jupiter</span><br>
包含 JUnit5.x 全新的编程模型和扩展机制
JUnit Vintage
用于在新的框架中兼容运行 JUnit3.x 和 JUnit4.x 的测试用例。
注解
@Test
@BeforeAll
@AfterAll
@Disabled
@Tag
@DisplayName
断言
常用的断言被封装在 org.junit.jupiter.api.Assertions 类
fail 断言测试失败
assertTrue/assertFalse 断言真假判断
assertEquals 判断是否相等
异常&日志
异常<br>
throw 、 throws 的区别<br>
throw 是方法内部抛出具体异常类对象的关键字, <br>throws用在方法 signature 上,表示方法调用者可以通过此方法声明向上抛出异常对象<br>无论采用哪种方式处理异常 , 都严禁捕获异常后什么都不做或打印一行日志了事<br>
分类<br>
Error
StackOverflowError、OutOfMemoryError
Exception
checked
unchecked
Collect
Get Started
Collect
Get Started
Collect
Get Started
Collect
Get Started
评论
0 条评论
下一页