JAVA網絡編程——BIO、NIO、AIO深度解析

I/O 一直是很多Java同學難以理解的一個知識點,這篇帖子將會從底層原理上帶你理解I/O,讓你看清I/O相關問題的本質。

1、I/O的概念

I/O 的全稱是Input/Output。雖常談及I/O,但想必你也一時不能給出一個完整的定義。搜索了谷哥欠,發現也盡是些冗長的論述。要想厘清I/O這個概念,我們需要從不同的視角去理解它。

1.1、計算機結構的視角

根據馮.諾依曼結構,計算機結構分為 5 大部分:運算器、控制器、存儲器、輸入設備、輸出設備。其中輸入是指將數據輸入到計算機的設備,比如鍵盤鼠標;輸出是指從計算機中獲取數據的設備,比如顯示器;以及既是輸入又是輸出設備,硬盤,網卡等。
在這里插入圖片描述
用戶通過操作系統才能完成對計算機的操作。計算機啟動時,第一個啟動的程序是操作系統的內核,它將負責計算機的資源管理和進程的調度。換句話說:操作系統負責從輸入設備讀取數據并將數據寫入到輸出設備。

1.2、程序應用的視角

根據大學里學到的操作系統相關的知識:為了保證操作系統的穩定性和安全性,一個進程的地址空間劃分為 用戶空間(User space) 和 內核空間(Kernel space ) 。

應用程序作為一個文件保存在磁盤中,只有加載到內存到成為一個進程才能運行。應用程序運行在計算機內存中,必然會涉及到數據交換,比如讀寫磁盤文件,訪問數據庫,調用遠程API等等。但我們編寫的程序并不能像操作系統內核一樣直接進行I/O操作。

從應用程序的視角來看的話,我們的應用程序對操作系統的內核發起 IO 調用(系統調用),操作系統負責的內核執行具體的 IO 操作。也就是說,我們的應用程序實際上只是發起了 IO 操作的調用而已,具體 IO 的執行是由操作系統的內核來完成的。

但操作系統向外提供API,其由各種類型的系統調用(System Call)組成,以提供安全的訪問控制。所以應用程序要想訪問內核管理的I/O,必須通過調用內核提供的系統調用(system call)進行間接訪問。

所以I/O之于應用程序來說,強調的通過向內核發起系統調用完成對I/O的間接訪問。換句話說應用程序發起的一次IO操作實際包含兩個階段:

  1. IO調用階段:應用程序進程向內核發起系統調用。
  2. IO執行階段:內核執行IO操作并返回。準備數據階段:內核等待I/O設備準備好數據;拷貝數據階段:將數據從內核緩沖區拷貝到用戶空間緩沖區。

UNIX 系統下, IO 模型一共有 5 種:

  • 同步阻塞 I/O、
  • 同步非阻塞 I/O、
  • I/O 多路復用、
  • 信號驅動 I/O
  • 異步 I/O。

推薦孫衛琴老師的書籍:
在這里插入圖片描述

2、BIO (Blocking I/O)

2.1、BIO模型解析

BIO即同步阻塞IO,實現模型為一個連接就需要一個線程去處理。這種方式簡單來說就是當有客戶端來請求服務器時,服務器就會開啟一個線程去處理這個請求,即使這個請求不干任何事情,這個線程都一直處于阻塞狀態。

應用程序中進程在發起IO調用后至內核執行IO操作返回結果之前,若發起系統調用的線程一直處于等待狀態,則此次IO操作為阻塞IO。阻塞IO簡稱BIO,Blocking IO。其處理流程如下圖所示:
在這里插入圖片描述
從上圖可知當用戶進程發起IO系統調用后,內核從準備數據到拷貝數據到用戶空間的兩個階段期間用戶調用線程選擇阻塞等待數據返回。

因此BIO帶來了一個問題:如果內核數據需要耗時很久才能準備好,那么用戶進程將被阻塞,浪費性能。為了提升應用的性能,雖然可以通過多線程來提升性能,但線程的創建依然會借助系統調用,同時多線程會導致頻繁的線程上下文的切換,同樣會影響性能。所以要想解決BIO帶來的問題,我們就得看到問題的本質,那就是阻塞二字。

BIO模型有很多缺點,最大的缺點就是資源的浪費。想象一下如果QQ使用BIO模型,當有一個人上線時就需要一個線程,即使這個人不聊天,這個線程也一直被占用,那再多的服務器資源都不管用。

2.2、BIO代碼演示

使用 BIO 模型編寫一個服務器端,監聽 6666 端口,當有客戶端連接時,就啟動一個線程與之通訊。

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** @title BIOServer* @description 測試* @author: yangyongbing* @date: 2023/12/7 11:45*/
public class BIOServer {public static void main(String[] args) throws IOException {ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();ServerSocket serverSocket = new ServerSocket(8888);System.out.println("服務器啟動了");while (true){System.out.println("線程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());//監聽,等待客戶端連接System.out.println("等待連接....");final Socket socket = serverSocket.accept();System.out.println("連接到一個客戶端");//一個客戶端連接就創建一個線程,并與之建立通訊newCachedThreadPool.execute(new Runnable() {@Overridepublic void run() {//與客戶端建立通訊handler(socket);}});}}public static void handler(Socket socket) {try {System.out.println("線程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());byte[] bytes = new byte[1024];// 通過socket 獲取輸入流InputStream inputStream = socket.getInputStream();// 循環的讀取客戶端發送的數據while (true) {System.out.println("線程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());System.out.println("read....");int index = inputStream.read(bytes);if (index != -1) {// 輸出客戶端發送的數據System.out.println(new String(bytes, 0, index));} else {break;}}} catch (Exception e) {e.printStackTrace();} finally {System.out.println("關閉和客戶端的連接");try {socket.close();} catch (IOException e) {e.printStackTrace();}}}
}

3、NIO (Non-blocking/New I/O)

3.1、NIO模型解析

Java NIO 全稱 Java non-blocking IO,是指 JDK 提供的新 API。從 JDK1.4 開始,Java 提供了一系列改進的輸入/輸出的新特性,被統稱為 NIO(即 NewIO),是同步非阻塞的。

就是用戶進程在發起系統調用時指定為非阻塞,內核接收到請求后,就會立即返回,然后用戶進程通過輪詢的方式來拉取處理結果。也就是如下圖所示:
在這里插入圖片描述
應用程序中進程在發起IO調用后至內核執行IO操作返回結果之前,若發起系統調用的線程不會等待而是立即返回,則此次IO操作為非阻塞IO模型。非阻塞IO簡稱NIO,Non-Blocking IO。

BIO是阻塞的,如果沒有多線程,BIO就需要一直占用CPU,而NIO則是非阻塞IO,NIO在獲取連接或者請求時,即使沒有取得連接和數據,也不會阻塞程序。NIO的服務器實現模式為一個線程可以處理多個請求(連接)。
NIO有幾個知識點需要掌握,Channel(通道),Buffer(緩沖區), Selector(多路復用選擇器):

  • Channel既可以用來進行讀操作,又可以用來進行寫操作。NIO中常用的Channel有FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel。
  • Buffer緩沖區用來發送和接受數據。
  • Selector 一般稱為選擇器或者多路復用器 。它是Java NIO核心組件中的一個,用于檢查一個或多個NIO Channel(通道)的狀態是否處于可讀、可寫。Java在NIO中使用Selector往往是將Channel注冊到Selector中,如下圖所示:
    在這里插入圖片描述
    然而,非阻塞IO雖然相對于阻塞IO大幅提升了性能,但依舊不是完美的解決方案,其依然存在性能問題,也就是頻繁的輪詢導致頻繁的系統調用,會耗費大量的CPU資源。比如當并發很高時,假設有1000個并發,那么單位時間循環內將會有1000次系統調用去輪詢執行結果,而實際上可能只有2個請求結果執行完畢,這就會有998次無效的系統調用,造成嚴重的性能浪費。有問題就要解決,那NIO問題的本質就是頻繁輪詢導致的無效系統調用。

2.2、NIO代碼演示

NIO服務端的執行過程是這樣的:

  • 創建一個ServerSocketChannel和Selector,然后將ServerSocketChannel注冊到Selector上
  • Selector通過select方法去輪詢監聽channel事件,如果有客戶端要連接時,監聽到連接事件
  • 通過channel方法將socketchannel綁定到ServerSocketChannel上,綁定通過SelectorKey實現
  • socketchannel注冊到Selector上,關聯讀事件
  • Selector通過select方法去輪詢監聽channel事件,當監聽到有讀事件時,ServerSocketChannel通過綁定的SelectorKey定位到具體的channel,讀取里面的數據。
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;/*** @title NIOServer* @description NIO測試* @author: yangyongbing* @date: 2023/12/7 12:02*/
public class NIOServer {public static void main(String[] args) throws IOException{//創建一個socket通道,并且設置為非阻塞的方式ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();serverSocketChannel.configureBlocking(false);serverSocketChannel.socket().bind(new InetSocketAddress(9000));//創建一個selector選擇器,把channel注冊到selector選擇器上Selector selector=Selector.open();serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while (true){System.out.println("等待事件發生");selector.select();System.out.println("有事件發生了");Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()){SelectionKey key = iterator.next();iterator.remove();handler(key);}}}private static void handler(SelectionKey key) throws IOException {if (key.isAcceptable()){System.out.println("連接事件發生");ServerSocketChannel serverSocketChannel= (ServerSocketChannel) key.channel();//創建客戶端一側的channel,并注冊到selector上SocketChannel socketChannel = serverSocketChannel.accept();socketChannel.configureBlocking(false);socketChannel.register(key.selector(),SelectionKey.OP_READ);}else if (key.isReadable()){System.out.println("數據可讀事件發生");SocketChannel socketChannel= (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);int len = socketChannel.read(buffer);if (len!=-1){System.out.println("讀取到客戶端發送的數據:"+new String(buffer.array(),0,len));}//給客戶端發送信息ByteBuffer wrap = ByteBuffer.wrap("hello world".getBytes());socketChannel.write(wrap);key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);socketChannel.close();}}}

客戶端代碼:NIO客戶端代碼的實現比BIO復雜很多,主要的區別在于,NIO的客戶端也需要去輪詢自己和服務端的連接情況:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;/*** @title NIOClient* @description NIOClient測試* @author: yangyongbing* @date: 2023/12/7 12:19*/
public class NIOClient {public static void main(String[] args) throws IOException {//配置基本的連接參數SocketChannel channel = SocketChannel.open();channel.configureBlocking(false);Selector selector = Selector.open();channel.connect(new InetSocketAddress("127.0.0.1", 9000));channel.register(selector, SelectionKey.OP_CONNECT);//輪詢訪問selectorwhile (true) {selector.select();Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();iterator.remove();//連接事件發生if (key.isConnectable()) {SocketChannel socketChannel = (SocketChannel) key.channel();//如果正在連接,則完成連接if (socketChannel.isConnectionPending()) {socketChannel.finishConnect();}socketChannel.configureBlocking(false);ByteBuffer buffer = ByteBuffer.wrap("客戶端發送的數據".getBytes());socketChannel.write(buffer);socketChannel.register(selector, SelectionKey.OP_READ);} else if (key.isReadable()) {//讀取服務端發送過來的消息read(key);}}}}private static void read(SelectionKey key) throws IOException {SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(512);int len = socketChannel.read(buffer);if (len != -1) {System.out.println("客戶端收到信息:" + new String(buffer.array(), 0, len));}}}

效果大概是這樣的:首先服務端等待事件發生,當客戶端啟動時,服務器端先接受到連接的請求,接著接受到數據讀取的請求,讀完數據后繼續等待。

NIO通過一個Selector,負責監聽各種IO事件的發生,然后交給后端的線程去處理。NIO相比與BIO而言,非阻塞體現在輪詢處理上。BIO后端線程需要阻塞等待客戶端寫數據,如果客戶端不寫數據就一直處于阻塞狀態。而NIO通過Selector進行輪詢已注冊的客戶端,當有事件發生時才會交給后端去處理,后端線程不需要等待。

3、AIO (Non-blocking/New I/O)

3.1、AIO模型解析

異步 IO 是基于事件和回調機制實現的,也就是應用操作之后會直接返回,不會堵塞在那里,當后臺處理完成,操作系統會通知相應的線程進行后續的操作。
在這里插入圖片描述
目前來說 AIO 的應用還不是很廣泛。Netty 之前也嘗試使用過 AIO,不過又放棄了。這是因為,Netty 使用了 AIO 之后,在 Linux 系統上的性能并沒有多少提升。

3.2、AIO代碼演示

AIO是在JDK1.7中推出的新的IO方式–異步非阻塞IO,也被稱為NIO2.0,AIO在進行讀寫操作時,直接調用API的read和write方法即可,這兩種均是異步的方法,且完成后會主動調用回調函數。簡單來講,當有流可讀取時,操作系統會將可讀的流傳入read方法的緩沖區,并通知應用程序;對于寫操作而言,當操作系統將write方法傳遞的流寫入完畢時,操作系統主動通知應用程序。

Java提供了四個異步通道:AsynchronousSocketChannel、AsynchronousServerSocketChannel、AsynchronousFileChannel、AsynchronousDatagramChannel。

服務器端代碼:AIO的創建方式和NIO類似,先創建通道,再綁定,再監聽。只不過AIO中使用了異步的通道。

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.concurrent.TimeUnit;/*** @title AIOServer* @description AIOServer測試* @author: yangyongbing* @date: 2023/12/7 12:41*/
public class AIOServer {public static void main(String[] args) {try {//創建異步通道AsynchronousServerSocketChannel serverSocketChannel=AsynchronousServerSocketChannel.open();serverSocketChannel.bind(new InetSocketAddress(8080));System.out.println("等待連接中");//在AIO中,accept有兩個參數,// 第一個參數是一個泛型,可以用來控制想傳遞的對象// 第二個參數CompletionHandler,用來處理監聽成功和失敗的邏輯//  如此設置監聽的原因是因為這里的監聽是一個類似于遞歸的操作,每次監聽成功后要開啟下一個監聽serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {//請求成功處理邏輯@Overridepublic void completed(AsynchronousSocketChannel result, Object attachment) {System.out.println("連接成功,處理數據中");//開啟新的監聽serverSocketChannel.accept(null,this);handlerData(result);}@Overridepublic void failed(Throwable exc, Object attachment) {System.out.println("失敗");}});try {TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);} catch (InterruptedException e) {e.printStackTrace();}} catch (IOException e) {e.printStackTrace();}}private static void handlerData(AsynchronousSocketChannel result) {ByteBuffer byteBuffer=ByteBuffer.allocate(1024);//通道的read方法也帶有三個參數//1.目的地:處理客戶端傳遞數據的中轉緩存,可以不使用//2.處理客戶端傳遞數據的對象//3.處理邏輯,也有成功和不成功的兩個寫法result.read(byteBuffer, byteBuffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer result, ByteBuffer attachment) {if (result>0){attachment.flip();byte[] array = attachment.array();System.out.println(new String(array));}}@Overridepublic void failed(Throwable exc, ByteBuffer attachment) {System.out.println("失敗");}});}
}

客戶端代碼:主要實現數據的發送功能

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Scanner;/*** @title AIOClient* @description AIOClient* @author: yangyongbing* @date: 2023/12/7 12:44*/
public class AIOClient {public static void main(String[] args) {try {AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));Scanner scanner = new Scanner(System.in);String next = scanner.next();ByteBuffer byteBuffer = ByteBuffer.allocate(1024);byteBuffer.put(next.getBytes());byteBuffer.flip();socketChannel.write(byteBuffer);} catch (IOException e) {e.printStackTrace();}}
}

4、總結

I/O 其關鍵點是要將應用程序的IO操作分為兩個步驟來理解:IO調用和IO執行。IO調用才是應用程序干的事情,而IO執行是操作系統的工作。在IO調用時,對待操作系統IO就緒狀態的不同方式,決定了其是阻塞或非阻塞模式;在IO執行時,線程或進程是否掛起等待IO執行決定了其是否為同步或異步IO。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/207280.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/207280.shtml
英文地址,請注明出處:http://en.pswp.cn/news/207280.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

區塊鏈創新應用場景不斷拓展,實現去中心化

小編介紹&#xff1a;10年專注商業模式設計及軟件開發&#xff0c;擅長企業生態商業模式&#xff0c;商業零售會員增長裂變模式策劃、商業閉環模式設計及方案落地&#xff1b;扶持10余個電商平臺做到營收過千萬&#xff0c;數百個平臺達到百萬會員&#xff0c;歡迎咨詢。 區塊…

【Vulnhub 靶場】【BuffEMR: 1.0.1】【簡單 - 中等】【20210831】

1、環境介紹 靶場介紹&#xff1a;https://www.vulnhub.com/entry/buffemr-101,717/ 靶場下載&#xff1a;https://download.vulnhub.com/buffemr/BuffEMR-v1.0.1.ova 靶場難度&#xff1a;簡單 - 中等 發布日期&#xff1a;2021年08月31日 文件大小&#xff1a;4.6 GB 靶場作…

為什么每個 Java 開發者都需要了解 Scala

前面我們一起回顧了第九期 Scala & Java Meetup 中最受關注的話題 —— jdk 并發編程的終極解決方案&#xff1a;虛擬線程&#xff0c;探討了這一新特性對包括 Scala 在內的響應式編程語言的影響。 本次 Meetup 的首位分享者 Chunsen&#xff0c;在加入 Tubi 成為 Scala 開…

【學習筆記】Burnside引理,Pólya定理及其應用

Burnside引理 書接上回&#xff0c;繼續深入研究在群作用下集合的軌道與穩定子群的相關性質 現在我們想要研究這樣一個問題&#xff1a; 有限群 G 在有限集合 S 上面有一個作用&#xff0c;那么 S 的 G ? 軌道條數是多少 有限群G在有限集合S上面有一個作用&#xff0c;那么…

【投稿優惠|檢索穩定】2024年信息系統、工程與數字化經濟國際會議(ICISEDE 2024)

2024年信息系統、工程與數字化經濟國際會議(ICISEDE 2024) 2024 International Conference on Information Systems and Engineering and the Digital Economy(ICISEDE 2024) [會議簡介] 2024 年信息系統、工程與數字化經濟國際會議(ICISEDE 2024)將于 2024 年 1 月 20 日在廈門…

Endnote在word中加入參考文獻及自定義參考文獻格式方式

第一部分&#xff1a;在word中增加引用步驟 1、先下載對應文獻的endnote引用格式&#xff0c;如在谷歌學術中的下載格式如下&#xff1a; 2、在endnote中打開存儲env的格式庫&#xff0c;導入對應下載的文件格式&#xff1a;file>import>file>choose,import對應文件&a…

IT外包駐場加速企業IT創新

隨著科技的快速發展&#xff0c;企業在追求創新和應用IT技術方面面臨挑戰。IT外包駐場服務成為許多企業的選擇&#xff0c;助力企業實現快速、高效的IT項目實施和應用。 IT外包駐場服務的主要目標是幫助企業在IT創新方面取得突破。這種服務模式不僅僅是提供技術支持&#xff0c…

3D建模基礎教程:模型UV講解

本篇文章將帶你探索3D建模中的模型UV。了解UV有助于你在3D建模中更好地進行紋理映射和材質應用&#xff0c;從而創建出更加逼真的3D場景。 uv坐標&#xff1a; UV坐標是用于映射紋理到3D模型表面的2D坐標。它們將2D紋理圖像映射到3D模型的3D空間上&#xff0c;使模型表面在渲…

配電室無人值守改造

配電室無人值守改造是通過運用先進的技術和設備&#xff0c;將傳統的需要人工值守的配電室改造成可以遠程監控和管理的智能化配電室&#xff0c;從而實現無人值守。這種改造可以提高配電室的安全性、可靠性和效率&#xff0c;降低運維成本。 建立智能監控系統&#xff1a;通過安…

Vue3選項式-基礎部分篇

Vue3選項式風格-基礎部分篇 簡介模板語法文本插值原始HTMLAttribute 綁定使用 JavaScript 表達式調用函數全局組件調用內置指令動態參數注意事項 data()data()深度響應 methods有狀態的methods(防抖) DOM更新時機計算屬性class和style綁定條件渲染列表渲染數組變換偵聽事件處理…

Linux 系統設置cpu頻率

source_code: https://github.com/emagii/cpufrequtils cpufreq-set - A small tool which allows to modify cpufreq settings.&#xff08;修改內存頻率的工具&#xff09; cpufreq-set allows you to modify cpufreq settings without having to type e.g. “/sys/devices…

echart中定義brush,默認狀態,觸發狀態

1.定義矩形選擇筆刷&#xff1a;brush 2.設置brush的默認狀態和選中邏輯

理解VAE(變分自編碼器)

1.貝葉斯公式 貝葉斯理論的思路是&#xff0c;在主觀判斷的基礎上&#xff0c;先估計一個值&#xff08;先驗概率&#xff09;&#xff0c;然后根據觀察的新信息不斷修正(可能性函數)。 P(A)&#xff1a;沒有數據B的支持下&#xff0c;A發生的概率&#xff0c;也叫做先驗概率。…

小視頻怎么做成二維碼?視頻二維碼3步生成

在日常工作和生活中經常會看到各種類型的小視頻、短視頻&#xff0c;比如網頁、抖音等等的視頻都是可以下載查看的。當我們想要將下載視頻分享給多個人看時&#xff0c;生成二維碼的方式會更加的方便&#xff0c;那么視頻如何生成二維碼呢&#xff1f;下面就將快捷生成二維碼的…

AI:90-基于深度學習的自然災害損害評估

?? 本文選自專欄:人工智能領域200例教程專欄 從基礎到實踐,深入學習。無論你是初學者還是經驗豐富的老手,對于本專欄案例和項目實踐都有參考學習意義。 ??? 每一個案例都附帶有在本地跑過的核心代碼,詳細講解供大家學習,希望可以幫到大家。歡迎訂閱支持,正在不斷更新…

第75講:MySQL數據庫MVCC多版本并發控制核心概念以及底層原理

文章目錄 1.當前讀與快照讀的基本概念1.1.當前讀的基本概念1.2.快照讀的基本概念 2.什么是MVCC多版本并發控制3.MVCC多版本并發控制依賴的三個組件重要概念3.1.MySQL表中三個隱式字段的概念3.2.undo log日志以及版本鏈的概念3.3.ReadView讀視圖的概念 4.MVCC實現多版本并發控制…

【FPGA】Verilog:BCD 加法器的實現

0x00 XOR 運算在 2 的補碼加減法中的應用 2 的補碼加減法的特點是&#xff0c;當從某個數中減去負數時&#xff0c;將其轉換為正數的加法來計算&#xff0c;并將減去正數的情況轉換為負數的加法來計算&#xff0c;從而將所有減法運算轉換為加法運算。在這種情況下&#xff0c;…

電商使用在線客服系統有什么好處?

互聯網發展越來越快&#xff0c;電商這個板塊也越加成熟&#xff0c;消費者選擇在線購物時會對比平臺&#xff0c;貨比三家&#xff0c;這個時候電商提供高效便捷且貼心的消費者服務就顯得尤為重要了&#xff0c;在線客服系統作為電商企業與消費者溝通的重要工具&#xff0c;其…

SpringAMQP 快速入門

SpringAMQP 快速入門 1. 創建項目2. 快速入門2.2.1 消息發送2.2.2 消息接收 3. 交換機3.1 Fanout Exchange&#xff08;扇出交換機&#xff09;3.1.1 創建隊列與交換機3.1.2 消息接收3.1.3 消息發送 3.2 Direct Exchange&#xff08;直連交換機&#xff09;3.2.1 創建交換機與隊…

Validate 驗證規則詳解

前言: 以前小編發過一篇Validate 驗證規則 如何使用的&#xff0c;沒有去將Validate 驗證規則的原理應用場景&#xff0c;這篇文章來完善一下。 不知道如何使用的朋友可以點擊下面傳送門 傳送門 講解: Validate 驗證規則通常指的是在 Web 開發中&#xff0c;使用驗證器&…