TCP报文头结构

1.TCP 报文头结构

2.报文头各个部分说明

2.1.Source Port和Destination Port:

分别占用16位,表示源端口号和目的端口号;用于区别主机中的不同进程, 而IP地址是用来区分不同的主机的,源端口号和目的端口号配合上IP首部中的源IP地址和目的IP地址就能唯一 的确定一个TCP连接;

2.2.Sequence Number:

用来标识从TCP发端向TCP收端发送的数据字节流,它表示在这个报文段中的的第一个数据 字节在数据流中的序号;主要用来解决网络报乱序的问题;

2.3.Acknowledgment Number:

32位确认序列号包含发送确认的一端所期望收到的下一个序号,因此,确认序号应 当是上次已成功收到数据字节序号加1。不过,只有当标志位中的ACK标志(下面介绍)为1时该确认序列号的字 段才有效。主要用来解决不丢包的问题;

2.4.Offset:

给出首部中32 bit字的数目,需要这个值是因为任选字段的长度是可变的。这个字段占4bit(最多能 表示15个32bit的的字,即4*15=60个字节的首部长度),因此TCP最多有60字节的首部。然而,没有任选字段, 正常的长度是20字节;

2.5.TCP Flags:

TCP首部中有6个标志比特,它们中的多个可同时被设置为1,主要是用于操控TCP的状态机的,依次 为URG,ACK,PSH,RST,SYN,FIN。每个标志位的意思如下:

2.6.URG:

此标志表示TCP包的紧急指针域(后面马上就要说到)有效,用来保证TCP连接不被中断,并且督促 中间层设备要尽快处理这些数据;

2.7.ACK:

此标志表示应答域有效,就是说前面所说的TCP应答号将会包含在TCP数据包中;有两个取值:0和1, 为1的时候表示应答域有效,反之为0;

2.8.PSH:

这个标志位表示Push操作。所谓Push操作就是指在数据包到达接收端以后,立即传送给应用程序, 而不是在缓冲区中排队;

2.9.RST:

这个标志表示连接复位请求。用来复位那些产生错误的连接,也被用来拒绝错误和非法的数据包;

3.0.SYN:

表示同步序号,用来建立连接。SYN标志位和ACK标志位搭配使用,当连接请求的时候,SYN=1, ACK=0;连接被响应的时候,SYN=1,ACK=1;这个标志的数据包经常被用来进行端口扫描。扫描者发送 一个只有SYN的数据包,如果对方主机响应了一个数据包回来 ,就表明这台主机存在这个端口;但是由于这 种扫描方式只是进行TCP三次握手的第一次握手,因此这种扫描的成功表示被扫描的机器不很安全,一台安全 的主机将会强制要求一个连接严格的进行TCP的三次握手;

3.1.FIN:

表示发送端已经达到数据末尾,也就是说双方的数据传送完成,没有数据可以传送了,发送FIN标志 位的TCP数据包后,连接将被断开。这个标志的数据包也经常被用于进行端口扫描。

3.2.Window:

窗口大小,也就是有名的滑动窗口。

蓝牙协议入门(四)应用层协议

1.irOBEX 红外对象交互协议

irOBEX 红外对象交互协议,简称 OBEX,使高层协议同时运作在蓝牙和红外的无线链路之上。主要操作指令包括:

  • 连接操作
  • 断开操作
  • Put 操作
  • Get 操作

1.1.连接操作

连接操作的操作码为: 0x80。

字节0 字节1、2 字节3 字节4 字节5、6 字节7~n
0x80 连接请求分组长度 OBEX版本号 标志 客户端可接收最大的OBEX分组长度 可选头

下面是一个连接操作的示例:

字节0 字节1、2 字节3 字节4 字节5、6 字节7~n
0x80 Connect命令 7 0x10 flags 65534 可选头

1.2.断开操作

断开操作的操作码为:0x81。

1.3.Put 操作

Put 操作的操作码为:0x02。

1.4.Get 操作

Get 操作的操作码为:0x03。

2.音频与电话控制协议

2.1.协议架构

蓝牙音频如上图协议栈所示:音频通过基带传输同步面向连接分组实现,没有以规范的形式给出,不是协议栈的一部分。
TCS_Binary 是一种基于分组电话控制二进制编码指令集,位于 L2CAP 之上。实现蓝牙无绳电话、对讲机功能。
RFCOMM 用于 AT 指令,拨号上网、蓝牙耳机、耳麦、传真通过 AT 发送指令控制。

2.2.音频部分

64kbps 电信级语言质量音频流
CVSD continuous variable slope delta 连续可变斜率增量。
PCM pulse code modulation
PCM 存在斜率效应。CVSD 使用音节压缩算法,编码步长根据信号斜率变化自动调整,是现在比较好的编码方案,提高语言的抗干扰能力。

2.3.电话控制部分

TCS-Binary 电话控制部分。

  • 电话呼叫

    ​呼叫控制、呼叫建立、呼叫拆除;

  • 组管理

    ​访问权限请求、分布式配置、快速内部成员访问。

3.网络封装协议 BNEP

BNEPBluetooth Network Encapsulation Protocol) 网络封装协议。为了使集成蓝牙技术的电脑、电话、PDA、家用电器等网络设备交换信息,需要在网络层统一数据分组。网络封装协议将来自不同网络的数据分组重新封装,通过 L2CAP 进行传输。BNEP 支持 Ipv4、Ipv6、IPX

4.音视频分发协议AVDTP

AVDTP (Audio/Video Distribution Transport Protocol)音视频分发传输协议。

AVDTP 信令列表:

命令 信令标识符 描述
AVDTP_DISCOVER 0x01 发现设备中的流端点
AVDTP_GET_CAPABILITIES 0x02 获得流端点的信息能力
AVDTP_SET_CONFIGURATION 0x03 对SEP进行配置
AVDTP_GET_CONFIGURATION 0x04 获取SEP当前配置
AVDTP_RECONFIGURE 0x05 对SEP重新配置
AVDTP_OPEN 0x06 成功的配置SEP后,打开流
AVDTP_START 0x07 流被打开:用于开始形成流;流暂停时:用于重新形成流。
AVDTP_CLOSE 0x08 对SEP关闭
AVDTP_SUSPEND 0x09 请求SEP暂停
AVDTP_SECURITY_CONTROL 0x0A 设置设备内容保护、或者请求设备状况信息
AVDTP_ABORT 0x0B 中断正在建立或者传输的过程
AVDTP_GET_ALL_CAPABILITIES
AVDTP_DELAYREPORT

5.音视频控制传输协议AVCTP

AVCTP 可以使音视频设备同时支持多个应用框架,每个应用框架定义了各自相应的消息格式与应用规则。

6.参考链接

蓝牙核心技术概述(五):蓝牙协议规范(irOBEX、BNEP、AVDTP、AVCTP)

Redis常用命令

1.字符串

1.1. SET 命令

1.2. GET 命令

1.3. GETSET 命令

1.4. INCR 自增命令

2.有序集合(Sorted Set)

2.1. ZADD 命令

2.2. ZCARD 数量统计命令

2.3. ZCOUNT 查找指定区间成员数命令

2.4. ZRANK 返回查找值排名命令

2.5. ZREM 移除元素命令

2.6. ZUNIONSTORE 并集命令

2.7. ZINTERSTORE 交集命令

2.8. ZRANGE 范围显示指令

3. 哈希(HashSet)

3.1. HSET 插入指令

3.2. HGET 读取指令

3.3. HGETALL 读取全部

3.4. HSETNX 如果不存在则赋值

3.5. HDEL 删除

4.列表(List)

4.1. PUSH(插入元素,分为左 LPUSH 和右 RPUSH)

4.2. LRANGE 输出某一段元素

4.3. POP(分为 LPOP 和 RPOP)

4.4. LLEN 输出列表长度

4.5. LINDEX 输出某位置元素

5.集合

5.1. SADD 插入命令

5.2. SCARD 统计个数命令

5.3. SDIFFSTORE 差集并存储

5.4. SUNION 并集(SUNIONSTORE 则存储)

5.5. SINTER 交集

5.6. SPOP 随机弹出

5.7. SMEMBERS 显示所有元素

Java基础之垃圾回收器

1. Serial 收集器

Serial 收集器是单线程垃圾回收器,它仅能使用单个线程去进行垃圾回收的工作,而且在进行垃圾回收时需要 “Stop the World”。也就是说,在它工作的同时,其他线程必须暂停运行。

2. Serial Old 收集器

Serial 收集器的老年代版本。一种用途是在 JDK 1.5 以及之前的版本中与Parallel Scavenge 收集器搭配使用,另一种用途就是作为CMS 收集器的后备预案,在并发收集发生Concurrent Mode Failure 时使用。

3. ParNew 收集器

ParNew 收集器是 Serial 收集器的多线程版本。因为功能强大的老年代收集器 CMS 收集器无法与 Parallel Scavenge 收集器配合工作,因此 CMS 收集器常与 ParNew 收集器配合使用。

4. Parallel Scavenge 收集器

Parallel Scavenge 收集器的特点是它的关注点与其他收集器不同,CMS 等收集器的关注点
是尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge 收集器的目标则是达到
一个可控制的吞吐量。

5. Parallel Old 收集器

Parallel OldParallel Scavenge 收集器的老年代版本,使用多线程和“标记-整理”算法。
这个收集器是在 JDK 1.6 中才开始提供的,在此之前,新生代的 Parallel Scavenge 收集器一直
处于比较尴尬的状态。 原因是,如果新生代选择了Parallel Scavenge 收集器,老年代除了
Serial Old 收集器外别无选择。

6. CMS 收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。

其工作步骤分为以下四步:

  • 初始标记(CMS initial mark)
  • 并发标记(CMS concurrent mark)
  • 重新标记(CMS remark)
  • 并发清除(CMS concurrent sweep)

其中,初始标记、 重新标记这两个步骤仍然需要“Stop The World”。 初始标记仅仅只是标记一下GC Roots 能直接关联到的对象,速度很快,并发标记阶段就是进行 GC RootsTracing 的过程,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。

由于整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以,从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。

7. G1收集器

G1 是一款面向服务端应用的垃圾收集器。 HotSpot 开发团队赋予它的使命是(在比较长期的)未来可以替换掉 JDK 1.5 中发布的 CMS 收集器。 与 CMS 收集器相比,G1具备如下特点。

  • 空间整合:与 CMS 的“标记—清理”算法不同,G1 从整体来看是基于“标记—整理”算法实现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现的,但无论如何,这两种算法都意味着 G1 运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。 这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次 GC
  • 可预测的停顿:这是 G1 相对于 CMS 的另一大优势,降低停顿时间是 G1CMS 共同的关注点,但 G1 除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时 Java(RTSJ)的垃圾收集器的特征了。

值得一提的是,在 G1 之前的其他收集器进行收集的范围都是整个新生代或者老年代,而 G1 不再是这样。 使用 G1 收集器时,Java 堆的内存布局就与其他收集器有很大差别,它将整个 Java 堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分 Region(不需要连续)的集合。

如果不计算维护Remembered Set的操作,G1收集器的运作大致可划分为以下几个步骤:

  • 初始标记(Initial Marking)
  • 并发标记(Concurrent Marking)
  • 最终标记(Final Marking)
  • 筛选回收(Live Data Counting and Evacuation)

前三个阶段与 CMS 收集器相似,最后在筛选回收阶段首先对各个Region 的回收价值和成本进行排序,
根据用户所期望的 GC 停顿时间来制定回收计划。

蓝牙协议入门(三)中介层协议

1.主机控制接口协议 HCI

常用蓝牙控制器接口数据分组分为三类,分别为:

  • 指令分组(0x01)
  • 事件分组(0x04)
  • 数据分组(ACL:0x02,SCO:0x03)

全部分组类型列举如下:

HCI 分组类型 RS232分组指示器
HCI指令分组 0x01
HCI ACL数据分组 0x02
HCI SCO数据分组 0x03
HCI事件分组 0x04
HCI错误消息分组 0x05
HCI协商分组 0x06

1.1.指令分组

如:Accpet Connection Request

  • Opcode 为:0x0409
  • 参数长度为: 07
    • 参数中蓝牙地址为:00:0d:fd:5f:16:9f
  • 角色为:从设备 0x01

则大端数据模式指令为:09 04 07 9f 16 5f fd 0d 00 01

1.2.事件分组

如:Accpet Connection Request

  • Opcode :0x0409
  • 状态: 0x00
  • 总长度: 4 字节
  • 命令状态:0x0f

则分组为: 04 0f 04 00 01 09 04

1.3.数据分组

ACL 数据分组(注:PB = Packet_BoundaryBC = Broadcast Flag

SCO 数据分组

2.逻辑链路控制与适配协议 L2CAP

L2CAP 位于基带之上,将基带的数据分组转换为便于高层应用的数据分组格式,并提供协议复用和服务质量交换等功能。L2CAP 只支持 ACL 数据传输,不支持 SCO 数据。L2CAP 本身不提供加强信道可靠性和保证数据完整性的机制,其信道的可靠性依靠基带提供。

L2CAP 主要实现以下功能:

  1. 协议复用:底层传输协议没有提供对高层协议的复用机制,因而 L2CAP 支持高层协议复用,L2CAP 层可以区分其上的 SDP、RFCOMM、TCS 等。
  2. 分段重组:L2CAP层帮助实现基带的短 PDU 和高层的长 PDU 相互传输,L2CAP 本身不完成任何 PDU 的分段重组,具体的分段重组有低层和高层来完成。
  3. 服务质量 Qualityof Serivce 信息的交换:蓝牙建立连接的过程中,L2CAP允许交互蓝牙所期望的服务质量,建立完成后,通过监视资源的使用情况,来保证服务质量。
  4. 组抽象L2CAP 忽略地址组概念,他只关心数据。

L2CAP 信道有三种类型:

  • 面向连接信道:Connection-Oriented CO,用于两个设备之间的数据通信。
  • 无连接信道:Connection-LessCL,用来向一组设备广播方式传输数据。CID为固定值:0x0002。
  • 信令信道:Signaling,用于创建 CO 通道,可以通过协商改变 CO 信道的特性。

协议数据单元:

3.服务发现协议 SDP

SDP两种服务发现模式:

  • 服务搜索:查询具有特定服务属性的服务;
  • 服务浏览:简单的浏览全部可用服务。

PDU (协议数据单元)格式为:

不同的 PDU ID 的意义为:

Value Parameter Descirption
0x00 Reserved 保留
0x01 SDP_ErrorResponse 错误响应
0x02 SDP_ServiceSearchRequest 服务搜索请求
0x03 SDP_ServiceSearchResponse 服务搜索响应
0x04 SDP_ServiceAttributeRequest 服务属性请求
0x05 SDP_ServiceAttributeResponse 服务属性响应
0x06 SDP_ServiceSearchAttributeRequest 服务搜索属性请求
0x07 SDP_ServiceSearchAttributeResponse 服务搜索属性响应
0x08-0xff Reserved 保留

4.串口仿真协议 RFCOMM

RFCOMM 是为建立在串口之上的传统应用提供环境接口,使他们可以做比较少协议改动就可以在蓝牙无线通信无线链路上工作。多路串口仿真是 RFCOMM 的重要功能,通过多路复用器(multiplexer),一条 L2CAP 链路可以同时 多个串行应用。

RFCOMM 两个蓝牙设备之间可以支持多达60多路仿真串口。RFCOMM 帧类型如下:

SABM 异步平衡模式设置指令
UA 未加编号的确认响应
DM 断开连接模式响应
DISC 断开连接指令
UIH 带头校验的未编号信息命令和响应

X.参考连接

蓝牙核心技术概述(四):蓝牙协议规范(HCI、L2CAP、SDP、RFOCMM)

TCP的三次握手与四次挥手

1.TCP 建立与断开流程

figure

2.为什么是三次握手

如果两次握手,那么只要客户端发送请求,就会建立连接。在这种情况下,如果有请求在信道上发生滞留,服务端就会收到一个失效的报文端,并误以为是客户端新的请求。由于客户端并不会对服务端的响应进行应答,所以在这种情况下,服务端以为连接建立了,而客户端并没有发出连接请求,所以永远也不会想服务端发送数据。

所以,三次握手是保证双方能够就发送数据这一问题达成一致所需要的最小值。

3.为什么是四次挥手

因为 TCP 是双工协议,当客户端没有想要发送的东西的时候,服务端可能还有想发送的东西。所以客户端只能单方面地承诺自己不再发送。而 TCP 真正的结束应该是服务端也承诺不再发送并得到应答才可以。

4.参考链接

TCP的三次握手(建立连接)和四次挥手(关闭连接)

TCP 为什么是三次握手,为什么不是两次或四次?

JVM中如何判断一个对象是否可以回收

1.根搜索算法

根搜索算法JVM 虚拟机判断一个对象是否存活的算法。它把内存中的每一个对象看成一个节点,并定义了一些对象作为根节点(GC Roots)。

如果一个对象中有对另一个对象中的引用,那么就认为第一个对象有一条指向第二个对象的边。JVM会起一个线程从所有的GC Roots开始往下遍历,当遍历完之后如果发现有一些对象不可到达,那么就认为这些对象已经没有用了,需要被回收。

2.哪些点可以被作为 GC Roots

2.1.Java 虚拟机栈中引用的对象

首先第一种是虚拟机栈中的引用的对象,我们在程序中正常创建一个对象,对象会在堆上开辟一块空间,同时会将这块空间的地址作为引用保存到虚拟机栈中,如果对象生命周期结束了,那么引用就会从虚拟机栈中出栈,因此如果在虚拟机栈中有引用,就说明这个对象还是有用的,这种情况是最常见的。

2.2.方法区中的静态成员

第二种是我们在类中定义了全局的静态的对象,也就是使用了static 关键字,由于虚拟机栈是线程私有的,所以这种对象的引用会保存在共有的方法区中,显然将方法区中的静态引用作为GC Roots是必须的。

2.3.方法区中的常量引用对象

第三种便是常量引用,就是使用了static final 关键字,由于这种引用初始化之后不会修改,所以方法区常量池里的引用的对象也应该作为GC Roots

2.4.本地方法区中的 JNI(Native 方法)引用的对象

最后一种是在使用 JNI 技术时,有时候单纯的 Java 代码并不能满足我们的需求,我们可能需要在 Java 中调用 CC++ 的代码,因此会使用 native方法,JVM内存中专门有一块本地方法栈,用来保存这些对象的引用,所以本地方法栈中引用的对象也会被作为GC Roots

3.参考链接

JVM如何判断一个Java对象是否可以回收

Linux常用指令

1.目录相关的命令

首先,我们要建立一个叫做 mydir 的文件夹,然后建立一个叫做 myfile 的文件。之后再把这个文件夹及其中的文件删除掉。

按住 Ctrl + Alt + T,打开一个端口,然后输入:

1
$ ls -l

通过 ls 命令,我们就可以查看当前目录下的文件和目录(要不然怎么知道建立和删除是否成功了呢,是吧)。然后我们开始建立文件夹。输入 :

1
$ mkdir mydir

看起来并没有发生什么,现在我们来 ls 一下看看我们是否成功建立了自己的文件夹。

好,既然我们已经有了自己的文件夹了,那就进去建立一个文件吧。进入文件夹的命令是:

1
$ cd mydir

进去之后,我们来创建一个文件,命令为:

1
$ vi myfile.txt

于是我们就进入了 vim 的界面。

关于 vim 的操作以前已经介绍过了,这里不再赘述。按下 i 进入插入模式,输入 hello 。然后按 Esc 到命令模式,输入:

1
:wq

保存退出。然后我们再 ls 一下,就能看见我们刚刚创建的文件了。

现在我们已经建立了新的文件,然后我们要删掉它。在 linux 中:

1
2
$ rm fliename // 删除文件
$ rmdir filename // 删除文件夹

但是我们如果碰到想要删除一个文件夹,而这个文件夹中又有很多文件的情况,处理起来就会很麻烦了。这时我们就需要一个新的命令:

1
$ rm -rf dirname //删除文件夹及其中所有文件

尝试一下,不过为了删除当前的目录,我们必须返回上一级目录中。输入:

1
$ cd ../

返回上级目录之后,删除该文件夹:

1
$ rm -rf mydir

这时整个文件夹已经被我们删除了。

2.字符串匹配 grep

grep 可以过滤出你想要的东西,举个例子,我想要在当前目录下找到所有名字中带 Mu 的文件或文件夹,那么我们就可以:

1
$ ls | grep Mu

3.进程相关指令 ps

ps 是显示进程的指令,可以根据不同的参数调节想要显示的进程种类,显示所有进程的指令是:

1
$ ps aux

加入我只想看跟自己相关的进程呢?后面加个 grep 就可以了。

1
$ ps aux | grep jack

4.查看文件

我们可以通过 cat 命令来查看文件内容:

1
$ cat fliename

当然,我们也可以通过 tail 显示文件的最后几行。如果加上参数 -f ,就能不断地读取最新的内容,达到实时监视的效果:

1
$ tail -f filename

5.参考链接

linux tail 命令详解

初窥Linux 之 我最常用的20条命令

MySQL基础之数据库的隔离级别

1.隔离级别

数据库的隔离级别有以下四种:

  1. 读未提交
  2. 读已提交
  3. 可重复读
  4. 串行化

2.数据库读取中的问题

隔离级别主要用来解决以下三个问题:

  1. 脏读
  2. 不可重复读
  3. 幻读

2.1.最简单情况

最简单的情况就是读未提交,其本质是:

事务在修改数据的时候只对数据增加行级共享锁。

事务在读数据的时候并未对数据加锁。

读未提交会引起脏读的问题。

2.2.脏读

假设 AB5000 块钱,修改了数据库当时还没有提交。此时 B 读取数据库中的自己账户的余额,以为自己收到了 5000 块钱。然而之后 A 并没有提交,而是回滚了该数据,所以最终 B 的账余额并没有增加。所以 B 读取到的是错误的信息,这就是脏读

为了避免这种情况,需要把数据库的隔离级别设置为读已提交。其本质是:

事务对当前被读取的数据加 行级共享锁(当读到时才加锁),一旦读完该行,立即释放该行级共享锁。

事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。

2.3.不可重复读

假设一个账户中有 5000 块钱, AB 同时从这个账户中取钱,当 A 查看余额时发现账户中有 5000 块钱,之后 B 取走了这 5000 块钱,当 A 再取钱的时候发现钱已经被取走了。也就是说,在同一事务中,A 对自己的账户进行的两次查询得到了不同的结果,这就是不可重复读

为了避免这种情况,需要把数据库的隔离级别设置为可重复读。其本质是:

事务在读取某数据的瞬间(就是开始读取的瞬间),必须先对其加 行级共享锁,直到事务结束才释放。

事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。

不可重复读脏读的区别在于,脏读读出的数据是无效的未提交的信息(脏读也称为无效数据读出)。而不可重复读两次读取的信息都是有效的已提交的信息,只不过在这两次读取期间数据被提交了而已。

可重复读读已提交最大的区别就在于事务在读的过程中行级共享锁的寿命。读已提交是读取完就释放,而可重复读是事务结束才释放,

2.4.幻读

假设 A 正在查询自己银行卡中的本月的消费记录,恰好此时 B 拿着 A 的信用卡刷了一笔钱。也就是说,在同一事务中,A 读到的两次消费记录不一致。新的结果中出现了幻影行,这就是幻读

为了避免这种情况,需要把数据库的隔离级别设置为串行化。其本质是:

事务在读取数据时,必须先对其加 表级共享锁 ,直到事务结束才释放;

事务在更新数据时,必须先对其加 表级排他锁 ,直到事务结束才释放。

幻读不可重复读的区别在于,不可重复读可以通过对读取到的数据加锁来避免。然而,你不可能给一条还未插入到数据库中的数据进行加锁,因此幻读是无法通过这种机制来避免,所以才要锁全表。

3.数据库的默认隔离级别

MySQL 的默认隔离级别是可重复读.

大多数数据库如 Sql Server , Oracle 的默认级别是读已提交。

4.参考链接

数据库事务隔离级别

深入分析事务的隔离级别

Java基础之Synchronized与ReentrantLock的区别

1.不同同步手段之间的区别

1.1.Synchronized

在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized 是很合适的。

  1. 原因在于,编译程序通常会尽可能的进行优化 synchronized
  2. synchronized 另外可读性非常好。

1.2.ReentrantLock

ReentrantLock 适用于资源竞争比较激烈的情况。

  1. 在资源竞争不激烈的情形下,性能稍微比synchronized差点点。
  2. 当同步非常激烈的时候,synchronized 的性能一下子能下降好几十倍。而 ReentrantLock 还能维持常态。
  3. 可以实现多样化的同步,比如有时间限制的同步,可以被 Interrupt 的同步等。

1.3.Atomic

Atomic 同样适用于资源竞争比较激烈的情况。

  1. 不激烈情况下,性能比synchronized 略逊。
  2. 资源竞争激烈的时候,也能维持常态。Atomic 的性能会优于ReentrantLock一倍左右。
  3. Atomic 有一个缺点,就是只能同步一个值,一段代码中只能出现一个Atomic 的变量,多于一个同步无效。因为他不能在多个Atomic之间同步。

2.RentranLock 的使用方式

  1. lock() ,如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
  2. tryLock() ,如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false。
  3. tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false。
  4. lockInterruptibly() ,如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断。

3.参考链接

Lock与synchronized 的区别