学习笔记11——并发编程之并发关键字
并发关键字
synchronized关键字
在应用Sychronized关键字时需要把握如下注意点:
1.一把锁只能同时被一个线程获取,没有获得锁的线程只能等待;
2.每个实例都对应有自己的一把锁(this),不同实例之间互不影响;例外:锁对象是*.class以及synchronized修饰的是static方法的时候,所有对象公用同一把锁
3.synchronized修饰的方法,无论方法正常执行完毕还是抛出异常,都会释放锁
synchronized关键字的基本用法
-
修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象; ---对象锁
synchronized (new Object()) {System.out.println("block1锁,我是线程" + Thread.currentThread().getName());try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("block1锁,"+Thread.currentThread().getName() + "结束");} synchronized (new Object()) {System.out.println("block2锁,我是线程" + Thread.currentThread().getName());try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("block2锁,"+Thread.currentThread().getName() + "结束");}
这种情况两个代码块获取的是两把锁
-
修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象; --对象锁
-
修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象; ---类锁
-
修饰一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。--类锁
-
修饰一个代码块,synchronized(),括号中如果是class对象,则获取的是类锁。
synchronized关键字的实现原理
synchronized
是 Java 中实现线程同步的核心机制,其底层实现基于 对象监视器(Monitor)、对象头中的锁状态标记 和 锁升级优化。以下从 JVM 层、操作系统层和硬件层逐步解析其实现原理。
1.对象监视器:
Monitor 是 JVM 实现同步的核心机制,每个对象关联一个 Monitor。其结构包括:
-
Owner:当前持有锁的线程。
-
EntryList:等待锁的线程队列(处于
BLOCKED
状态)。 -
WaitSet:调用
wait()
后进入等待的线程队列(处于WAITING
或TIMED_WAITING
状态)。
Monitor 的工作流程:
-
线程尝试通过 CAS 修改对象头获取锁:
-
若成功,设置 Owner 为当前线程,进入临界区。
-
若失败,线程进入 EntryList 阻塞等待。
-
-
释放锁时,Owner 清空,唤醒 EntryList 中的线程重新竞争。
从图中可以看出,每个线程对Object对象的访问,首先要获得Object的监视器,如果获取失败,该线程就进入同步状态,线程状态变为BLOCKED,当Object的监视器占有者释放后,在同步队列中得线程就会有机会重新获取该监视器。
2.对象头与状态标记:
每个Java对象在内存中分为3部分
-
对象头(Header):存储锁状态、GC 信息、哈希码等。
-
实例数据(Instance Data):对象的成员变量。
-
对齐填充(Padding):确保对象按 8 字节对齐。
对象头结构
-
Mark Word(64 bits):存储锁状态、线程 ID、GC 分代年龄等。
-
Klass Pointer(64 bits):指向类元数据的指针。
3.锁升级优化
synchronized实现的同步锁,1.6之前称之为重量级锁,重量锁会直接使用操作系统的底层的锁,会造成线程排队(串行执行),且会使CPU在用户态和核心态之间频繁切换,所以代价高、效率低。为了提高效率,从Java 1.6开始,JVM进行了优化,synchronized不一定直接使用重量锁,一共有四种状态:无锁、偏向锁、轻量级锁和重量级锁。
锁膨胀方向:无锁——>偏向锁——>轻量级锁——>重量级锁 锁只可以升级不可降级
偏向锁:
核心思想:偏向于第一个获取锁的线程,锁对象会记住首次访问它的线程 ID(记录在对象头的 Mark Word 中)。若后续没有其他线程竞争,该线程再次进入同步块时 无需加锁/解锁操作,仅需检查线程 ID 是否匹配。
1. 初次获取锁
-
CAS 设置线程 ID:通过 CAS 操作将当前线程 ID 写入对象的 Mark Word。
-
标记为偏向模式:对象头中的锁标志位更新为偏向锁状态(
101
)。
2. 再次进入同步块
-
检查线程 ID:判断对象头中的线程 ID 是否与当前线程一致:
-
✅ 一致:直接执行代码,无任何同步开销。
-
❌ 不一致:检查对象是否仍可偏向:
-
可偏向:尝试通过 CAS 竞争锁(重新偏向)。
-
已偏向其他线程:触发 偏向锁撤销,可能升级为轻量级锁。
-
-
偏向锁是针对于单个线程而言的,线程获得锁之后就不会再有解锁等操作
了,这样可以省略很多开销。假如有两个线程来竞争该锁话,那么偏向锁就失效了,进而升级成轻量级锁了
轻量锁: JVM 在存在低强度线程竞争时采用的锁优化机制,其核心是通过 CAS 自旋 减少线程阻塞的开销,避免直接升级为重量级锁(操作系统互斥锁)。
核心思想:通过线程的 CAS 自旋(循环尝试获取锁)替代直接阻塞线程,降低上下文切换的开销。适用于 线程交替执行同步块、竞争短暂且稀疏 的场景。
1. 加锁过程
-
创建 Lock Record
-
线程进入同步块时,在栈帧中分配一个 Lock Record,用于保存锁对象的原始 Mark Word。
-
-
CAS 竞争锁
-
尝试通过 CAS 操作将对象头的 Mark Word 更新为指向 Lock Record 的指针:
-
✅ 成功:对象头的锁标志位变为
00
(轻量级锁状态),线程获得锁。 -
❌ 失败:说明存在竞争,触发 自旋重试 或 锁升级。
-
-
2. 解锁过程
-
CAS 还原 Mark Word
-
通过 CAS 将 Lock Record 中保存的原始 Mark Word 写回对象头。
-
-
还原成功
-
对象恢复为无锁状态(标志位
01
)。
-
-
还原失败
-
说明锁已升级为重量级锁,需通过操作系统级别的锁机制释放。
-
轻量级锁的升级与自旋优化
所谓自旋,就是指当有另外一个线程来竞争锁时,这个线程会在原地循环等待,而不是把该线程给阻塞,直到那个获得锁的线程释放锁之后,这个线程就可以马上获得锁的。注意,锁在原地循环的时候,是会消耗cpu的,就相当于在执行一个啥也没有的for循环。所以,轻量级锁适用于那些同步代码块执行的很快的场景,这样,线程原地等待很短很短的时间就能够获得锁了。但是也有问题,1.如果同步代码块执行的很慢,需要消耗大量的时间,那么这个时侯,其他线程在原地等待空消耗cpu。2.本来一个线程把锁释放之后,当前线程是能够获得锁的,但是假如这个时候有好几个线程都在竞争这个锁的话,那么有可能当前线程会获取不到锁,还得原地等待继续空循环消耗cup,甚至有可能一直获取不到锁
1. 自旋策略
-
固定次数自旋:早期 JVM 采用固定次数的自旋(如 10 次),若失败则升级为重量级锁。
-
自适应自旋(Adaptive Spinning):JDK 1.6 后引入,根据 历史自旋成功率动态调整自旋次数(如上次成功则增加次数,失败则减少)。
2. 升级条件
-
自旋失败:多次 CAS 尝试后仍无法获取锁。
-
竞争加剧:超过 JVM 自旋阈值(由
-XX:PreBlockSpin
控制,默认值因 JVM 实现而异)。 -
升级路径:轻量级锁 → 重量级锁(线程阻塞,依赖操作系统互斥量)。
重量锁:是 JVM 在锁竞争激烈时的最终锁机制,其核心是 依赖操作系统互斥量(Mutex)和条件变量(Condition Variables)实现线程同步,通过线程阻塞和唤醒机制解决高并发竞争问题。当轻量级锁自旋失败(线程竞争激烈)时,升级为重量级锁,通过 操作系统内核调度 管理线程阻塞与唤醒。操作系统级别的锁机制通常支持公平性策略(如 FIFO 队列),避免线程饥饿。
synchronized关键字的特性
-
可重入性:又称递归锁,同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁。前提锁对象是同一个对象或class,不会因为之前已经获取就阻塞。
public class ReentrantDemo {public synchronized void methodA() {methodB(); // 可重入:直接进入 methodB 的同步块}public synchronized void methodB() {// 代码逻辑} }
-
内存可见性:线程释放锁时,会将共享变量的修改刷新到主内存;获取锁时,会从主内存重新加载变量值
-
有序性:禁止指令重排序:临界区内的代码不会被编译器或处理器重排序破坏逻辑。
synchronized关键字的优化策略
1. 锁消除(Lock Elimination)
-
触发条件:JIT 编译器检测到不存在共享数据竞争的锁。
-
示例:局部对象锁(线程私有,无需同步)。
public void lockEliminationDemo() {Object localLock = new Object();synchronized (localLock) { // 锁被消除System.out.println("This lock is unnecessary");} }
2. 锁粗化(Lock Coarsening)
-
触发条件:多次连续的锁操作合并为一次,减少锁开销。
-
示例:循环内重复加锁。
public void lockCoarseningDemo() {synchronized (this) {// 合并多次锁操作为一次for (int i = 0; i < 100; i++) {// 操作共享资源}} }
3. 自旋优化(Adaptive Spinning)
-
轻量级锁失败后:线程不立即阻塞,而是自旋重试(默认次数为 10 次,JDK 6 后改为自适应)。
volatile关键字
volatile
是 Java 中用于解决 多线程内存可见性 和 指令重排序 问题的关键字。它提供了一种轻量级的同步机制,确保变量的修改对所有线程立即可见,同时禁止编译器和处理器对代码进行某些优化。注意不保证复合操作的原子性,需结合锁或原子类使用。
volatile的核心作用
-
保证可见性:在多线程环境下,每个线程可能将共享变量缓存到自己的 工作内存(CPU 缓存) 中,导致一个线程修改了变量的值,其他线程无法立即看到最新值;若使用volatile修饰变量,每次读写都直接操作 主内存,绕过线程的工作内存。强制其他线程在读取
volatile
变量时,清空本地缓存,重新从主内存加载最新值。public class VisibilityDemo {private volatile boolean flag = false; public void writer() {flag = true; // 写操作立即刷新到主内存} public void reader() {while (!flag) { // 每次读取都从主内存加载最新值// 循环等待}System.out.println("Flag is now true");} }
-
禁止指令重排序
-
问题背景: 编译器和处理器为了提高性能,可能会对代码执行顺序进行 重排序(如单例模式中的双重检查锁定问题)。
-
volatile 的解决方案:
-
通过插入 内存屏障(Memory Barrier),禁止对
volatile
变量前后的指令进行重排序。 -
确保
volatile
变量的写操作对其他线程可见的顺序符合程序预期。
-
public class Singleton {private static volatile Singleton instance; public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton(); // 禁止重排序,确保对象完全初始化}}}return instance;} }
-
volatile的实现原理
1. 内存屏障(Memory Barrier):又称内存栅栏,是一个cpu指令。
JVM 会在 volatile
变量的读写操作前后插入特定类型的内存屏障,确保以下两点:
-
可见性:强制将工作内存的修改刷新到主内存,或从主内存重新加载变量值。
-
有序性:禁止编译器或处理器对指令进行重排序。
为了提高处理速度,处理器不直接和内存进行通信,而是先将系统内存的数据读到内部缓存(L1、L2或其他)后再进行操作,但操作完不知道何时会写到内存。如果对声明了volatile的变量进行写操作,JVM就会向处理器发送一条lock前缀的指令,将这个变量所在缓存行的数据写到系统内存。为了保证各个处理器的缓冲是一致的,实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓冲的值是不是过期了,当处理器发现自己缓存行对应的 内存的地址被修改了,就会将当前处理器的缓存行设置为无效状态,当处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据读到处理器缓冲中。所有多核处理器发现本地缓存失效后,就会从内存中重读该变量的数据,也就获取到了最新的值。
屏障类型 | 作用 |
---|---|
LoadLoad | 确保当前读操作之前的其他读操作已完成。 |
StoreStore | 确保当前写操作之前的其他写操作对其他线程可见。 |
LoadStore | 确保当前读操作之后的写操作不会被重排序到读操作之前。 |
StoreLoad | 确保当前写操作之后的所有读/写操作不会被重排序到写操作之前(全能屏障,开销最大)。 |
2. 具体规则
-
写操作(Write): 在写
volatile
变量后插入 StoreStore 和 StoreLoad 屏障,确保:-
当前变量的修改对其他线程可见。
-
写操作不会被重排序到后续操作之后。
-
-
读操作(Read): 在读
volatile
变量前插入 LoadLoad 和 LoadStore 屏障,确保:-
后续操作不会被重排序到读操作之前。
-
每次读取都能获取最新值。
-
volatile的使用场景
1.状态标志位
多线程中通过 volatile
变量作为开关控制线程执行。
public class TaskRunner implements Runnable {private volatile boolean running = true; public void stop() {running = false; // 其他线程调用此方法后,立即停止任务} @Overridepublic void run() {while (running) {// 执行任务}} }
2.单例模式
通过 volatile
解决双重检查锁定中的重排序问题
3.无锁编程
volatile 与 synchronized 的对比
维度 | volatile | synchronized |
---|---|---|
可见性 | 保证变量的可见性 | 保证临界区内所有变量的可见性 |
原子性 | 仅单次读/写操作原子 | 保证代码块内操作的原子性 |
有序性 | 禁止指令重排序 | 通过锁机制隐式保证有序性 |
性能 | 轻量级(无上下文切换开销) | 重量级(涉及锁升级和线程阻塞) |
适用场景 | 状态标志、单次发布、无锁编程 | 复杂同步逻辑、复合操作 |
final关键字
final的作用
修饰类:禁止类被继承(即不可有子类)。
修饰方法:禁止方法被子类重写(Override)。但是可以重载。
修饰变量:
-
基本类型变量:变量值不可修改,必须在声明时或构造方法中初始化。
final int MAX_VALUE = 100; // 声明时初始化 final double PI; public MyClass() { PI = 3.14; } // 构造方法中初始化
-
引用类型变量:引用指向的对象不可变,但对象内部状态可能可变。
final List<String> list = new ArrayList<>(); list.add("Java"); // 允许操作对象内容 // list = new LinkedList<>(); // 编译错误,禁止重新赋值
-
常量定义:全局常量
public static final String LOG_TAG = "System";
相关文章:

学习笔记11——并发编程之并发关键字
并发关键字 synchronized关键字 在应用Sychronized关键字时需要把握如下注意点: 1.一把锁只能同时被一个线程获取,没有获得锁的线程只能等待; 2.每个实例都对应有自己的一把锁(this),不同实例之间互不影响;例外:锁…...

2.2 Windows本地部署DeepSeek模型 --- Ollama篇(下)
2.3Ollama加载已下载Deepseek模型 无网络连接,直接通过Ollama本地已经本地已经下载好的的Deepseek模型。 2.3.1 查看已安装模型 PS C:\Users\Administrator> ollama list NAME ID SIZE MODIFIED deepseek-r1:8…...

DeepSeek R1-32B医疗大模型的完整微调实战分析(全码版)
DeepSeek R1-32B微调实战指南 ├── 1. 环境准备 │ ├── 1.1 硬件配置 │ │ ├─ 全参数微调:4*A100 80GB │ │ └─ LoRA微调:单卡24GB │ ├── 1.2 软件依赖 │ │ ├─ PyTorch 2.1.2+CUDA │ │ └─ Unsloth/ColossalAI │ └── 1.3 模…...

mysql的锁--一篇读懂所有锁机制
目录 mysql的锁 概述:根据mysql锁的大类型可以分为 我们先来讲一下范围最大的全局锁 使用 为什么要使用全局锁? 使用全局锁进行备份的缺点 表级锁 表锁 1.共享读表锁的语法 2.排斥写表锁 元数据锁 意向锁 什么是意向锁 怎么产生意向锁 意向…...

LTC6804、LTC6811、LTC6813的使用
FSEC自制BMS第一步:从零开发使用LTC6804采集电池电压 LTC6811特性 LTC6811 是 LTC6804 的引脚兼容型升级器件,LTC6804官方已经不推荐选用 可测量多达 12 节串联电池 1.2mV 最大总测量误差 可堆叠式架构能支持几百个电池 内置 isoSPI™ 接口 可在 290μ…...

linux内存页块划分及位图存储机制
page_alloc.c - mm/page_alloc.c - Linux source code v5.4.285 - Bootlin Elixir Cross Referencer 一. 什么是页块(Pageblock)? 定义:页块是物理内存中的一个连续区域,由 2^pageblock_order 个物理页(Pag…...

Vue 文件下载功能的跨域处理与前后端实现详解
在 Web 应用开发中,文件下载功能是常见需求。但由于跨域限制和认证机制的复杂性,实际开发中常遇到下载失败或权限错误等问题。本文将结合 Vue 前端和 Spring Boot 后端,详细介绍文件下载功能的实现与跨域问题的解决方案。 一、问题背景 在某…...

boost::beast websocket 实例
环境:ubuntu 1. 安装boost sudo apt install -y libboost-all-dev 2. Server端 #include <boost/asio.hpp> #include <boost/beast.hpp> #include <iostream> #include <thread>namespace beast boost::beast; // 从 Boost.Beast 中导…...

复试难度,西电卓越工程师学院(杭研院)考研录取情况
01、卓越工程师学院各个方向 02、24卓越工程师学院(杭研院)近三年复试分数线对比 PS:卓越工程师学院分为广研院、杭研院 分别有新一代电子信息技术、通信工程、集成电路工程、计算机技术、光学信息工程、网络信息安全、机械,这些…...

Rabbitmq--延迟消息
13.延迟消息 延迟消息:生产者发送消息时指定一个时间,消费者不会立刻收到消息,而是在指定时间之后才会收到消息 延迟任务:一定时间之后才会执行的任务 1.死信交换机 当一个队列中的某条消息满足下列情况之一时,就会…...

cocos creator使用mesh修改图片为圆形,减少使用mask,j减少drawcall,优化性能
cocos creator版本2.4.11 一个mask占用drawcall 3个以上,针对游戏中技能图标,cd,以及多玩家头像,是有很大优化空间 1.上代码,只适合单独图片的,不适合在图集中的图片 const { ccclass, property } cc._decorator;c…...

C++ Qt开发成长之路,从入门到企业级实战项目,保姆级学习路线
Qt 介绍 Qt是一个跨平台的C图形用户界面应用程序开发框架,最初由挪威的Trolltech公司开发,后来被诺基亚收购,现在由Qt公司维护。它提供了丰富的工具和类库,使开发者能够轻松地创建各种类型的应用程序,包括桌面应用、移…...

JavaWeb后端基础(7)AOP
AOP是Spring框架的核心之一,那什么是AOP?AOP:Aspect Oriented Programming(面向切面编程、面向方面编程),其实说白了,面向切面编程就是面向特定方法编程。AOP是一种思想,而在Spring框…...

Uniapp实现地图获取定位功能
摘要:本文将手把手教你如何在Uniapp项目中集成地图功能、实现定位获取,并解决微信小程序、APP、H5三端的兼容性问题🚀🚀🚀 一、环境准备 地图平台选择 微信小程序:腾讯地图(强制使用)…...

批量将 Excel 转换 PDF/Word/CSV以及图片等其它格式
Excel 格式转换是我们工作过程当中非常常见的一个需求,我们通常需要将 Excel 转换为其他各种各样的格式。比如将 Excel 转换为 PDF、比如说将 Excel 转换为 Word、再比如说将 Excel文档转换为图片等等。 这些操作对我们来讲都不难,因为我们通过 Office 都…...
Flutter:StatelessWidget vs StatefulWidget 深度解析
目录 1. 引言 2. StatelessWidget(无状态组件) 2.1 定义与特点 2.2 代码示例 3. StatefulWidget(有状态组件) 3.1 定义与特点 3.2 代码示例 4. StatelessWidget vs StatefulWidget 对比 5. StatefulWidget 生命周期 5.1…...

Stream流学习
Stream流 把数据放进stream流水线,对数据进行一系列操作(中间方法),最后封装(终结方法)。 Stream.of()允许传入任何参数 常见中间方法 可以对数据进行链式(流水线)操作,但…...

多视图几何--恢复相机位姿/内参的几种方法
恢复相机位姿的几种方法 1分解投影矩阵 1.1投影矩阵分解为相机内外参矩阵的完整解析 投影矩阵(Projection Matrix)是计算机视觉中将三维世界点映射到二维像素坐标的核心工具,其本质是相机内参矩阵(Intrinsic Matrix)…...

[数据结构]堆详解
目录 一、堆的概念及结构 二、堆的实现 1.堆的定义 2堆的初始化 3堆的插入 编辑 4.堆的删除 5堆的其他操作 6代码合集 三、堆的应用 (一)堆排序(重点) (二)TOP-K问题 一、堆的概念及结构 堆的…...

领域驱动设计(DDD)与MVC架构:理念对比与架构选择
领域驱动设计(DDD)与MVC架构:理念对比与架构选择 一、架构之争的本质:业务复杂度驱动技术演进 在软件开发领域,没有银弹式的完美架构,只有适合当前业务场景的合理选择。MVC与DDD的区别本质上是业务复杂度与…...

牛客周赛:84:B:JAVA
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网 题目描述 import java.util.ArrayList; import java.util.Arrays; import java.util.Scanner; public class Main {public static void main(String[] args) {Scanner scanner new Scanner(S…...

【理想解法学习笔记】
目录 理想解法原理简介算法步骤属性值规范化方法代码示例 理想解法 原理简介 TOPSIS(Technique for Order Preference by Simi larity to IdealSolution)法是一种逼近理想解的排序方法。其基本的处理思路是:首先建立初始化决策矩阵,而后基于规范化后的初…...

CI/CD—Jenkins配置一次完整的jar自动化发布流程
背景: 实现设想: 要创建自动化发布,需要准备一台测试服务器提前安装好java运行所需的环境,JDK版本最好和Windows开发机器上的版本一致,在Jenkins上配置将构建好的jar上传到测试服务器上,测试服务器自动启动…...

Magento2根据图片文件包导入产品图片
图片包给的图片文件是子产品的图片,如下图:A104255是主产品的sku <?php/*** 根据图片包导入产品图片,包含子产品和主产品* 子产品是作为主图,主产品是作为附加图片*/use Magento\Framework\App\Bootstrap;include(../app/boot…...

从零开始的python学习(五)P71+P72+P73+P74
本文章记录观看B站python教程学习笔记和实践感悟,视频链接:【花了2万多买的Python教程全套,现在分享给大家,入门到精通(Python全栈开发教程)】 https://www.bilibili.com/video/BV1wD4y1o7AS/?p6&share_sourcecopy_web&v…...

OpenHarmony5.0分布式系统源码实现分析—软总线
一、引言 OpenHarmony 作为一款面向万物互联的操作系统,其分布式软总线(Distributed SoftBus)是实现设备间高效通信和协同的核心技术之一。分布式软总线通过构建一个虚拟的总线网络,使得不同设备能够无缝连接、通信和协同工作。本…...

基于SpringBoot实现旅游酒店平台功能六
一、前言介绍: 1.1 项目摘要 随着社会的快速发展和人民生活水平的不断提高,旅游已经成为人们休闲娱乐的重要方式之一。人们越来越注重生活的品质和精神文化的追求,旅游需求呈现出爆发式增长。这种增长不仅体现在旅游人数的增加上࿰…...

代码随想录算法训练营第六十一天 | 108. 冗余连接 109. 冗余连接II
108. 冗余连接 题目链接:KamaCoder 文档讲解:代码随想录 状态:AC Java代码: import java.util.*;class Main {public static int[] father;public static void main(String[] args) {Scanner scan new Scanner(System.in);int n…...

RoboVQA:机器人多模态长范围推理
23 年 11 月来自 Google Deepmind 的论文“RoboVQA: Multimodal Long-Horizon Reasoning for Robotics”。 本文提出一种可扩展、自下而上且本质多样化的数据收集方案,该方案可用于长期和中期的高级推理,与传统的狭窄自上而下的逐步收集相比,…...

TCP/IP原理详细解析
前言 TCP/IP是一种面向连接,可靠的传输,传输数据大小无限制的。通常情况下,系统与系统之间的http连接需要三次握手和四次挥手,这个执行过程会产生等待时间。这方面在日常开发时需要注意一下。 TCP/IP 是互联网的核心协议族&…...