2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Décrit les connaissances de base de NIO au niveau ava, pour un examen de base
En Java, NIO (Non-blocking I/O ou New I/O) est un nouvel ensemble d'API d'opération d'entrée/sortie introduit dans Java SE 1.4 et les versions ultérieures.
Par rapport au modèle IO traditionnel, il offre une plus grande efficacité et de meilleures capacités de traitement simultané. La principale caractéristique de NIO est sa fonctionnalité non bloquante, qui permet à un seul thread de gérer plusieurs canaux d'E/S, améliorant ainsi considérablement les performances des applications dans des scénarios à forte concurrence.
Le scénario d'utilisation de Java NIO est particulièrement adapté aux situations où un grand nombre de connexions simultanées doivent être traitées. Par exemple, dans un serveur réseau, un thread peut gérer des milliers de connexions client sans qu'il soit nécessaire d'allouer un thread indépendant pour chaque connexion. .Peut réduire considérablement la consommation des ressources du système et améliorer les capacités de traitement.
Le canal est le support de flux de données dans Java NIO. Il est bidirectionnel et peut être utilisé pour lire ou écrire des données. Le principal avantage des canaux est qu'ils ne sont pas bloquants, ce qui signifie qu'un thread peut gérer plusieurs canaux. Lorsqu'aucun événement ne se produit sur un canal, le thread ne sera pas bloqué et pourra gérer d'autres tâches. Les principaux types de canaux comprennent :
FileChannel
: Utilisé pour les opérations de lecture et d'écriture de fichiers. Il peut être utilisé pour écrire des données du tampon dans le fichier ou pour lire des données du fichier dans le tampon.SocketChannel
: Utilisé pour les connexions TCP dans les communications réseau et peut être utilisé pour lire et écrire des données.ServerSocketChannel
: Utilisé pour accepter de nouvelles connexions SocketChannel, similaires au ServerSocket traditionnel.DatagramChannel
: Utilisé pour la communication UDP, qui peut envoyer et recevoir des datagrammes.Buffer est un objet utilisé pour stocker des données dans NIO. Il s'agit d'un tableau d'octets qui peut écrire et lire des données. Un tampon a une capacité spécifique et possède deux propriétés importantes : la position et la limite.
Les principaux types de tampons sontByteBuffer
、CharBuffer
、ShortBuffer
、IntBuffer
、LongBuffer
、FloatBuffer
etDoubleBuffer
, chaque type correspond à un type de données primitif.
Un sélecteur est un multiplexeur dans NIO qui permet à un seul thread de surveiller les événements de plusieurs canaux, tels que les événements de lecture, d'écriture, de connexion et de réception. Le sélecteur avertit l'application lorsqu'un des canaux est prêt pour les opérations d'E/S. L'utilisation de sélecteurs améliore considérablement les capacités de traitement simultané des applications réseau car il n'est pas nécessaire de créer un thread pour chaque connexion.
Les principales méthodes de sélection comprennent :
select()
: Bloque jusqu'à ce qu'au moins un canal soit prêt pour les opérations d'E/S.selectedKeys()
: Renvoie un Set contenant les objets SelectionKey de tous les canaux préparés.wakeup()
: Interruption bloquéeselect()
transfert.SelectionKey
Il s'agit de l'association entre les sélecteurs et les canaux. Il représente le statut d'enregistrement d'un canal sur le sélecteur, y compris les canaux, les sélecteurs, les collections d'événements intéressées et les collections d'événements prêtes.
Il s'agit du modèle d'E/S le plus traditionnel. En Java, les API Socket et ServerSocket traditionnelles sont basées sur le blocage des E/S. Dans ce mode, lorsqu'un thread lance une opération de lecture ou d'écriture, le thread est bloqué jusqu'à ce que l'opération d'E/S soit terminée. S'il n'y a aucune donnée à lire pour l'opération de lecture, ou si l'opération d'écriture ne peut pas être terminée immédiatement, le thread attendra que l'opération soit terminée.
Caractéristiques:
Les E/S non bloquantes font partie du framework Java NIO (New I/O), qui permet aux threads de lancer des opérations de lecture et d'écriture sans être bloqués. S'il n'y a aucune donnée à lire pour une opération de lecture, ou si l'opération d'écriture ne peut pas être terminée immédiatement, le thread ne sera pas suspendu et pourra continuer à effectuer d'autres tâches.
Caractéristiques:
Le multiplexage consiste à surveiller plusieurs descripteurs de fichiers en même temps via un seul thread et à n'opérer sur un descripteur que lorsqu'il est prêt (cela signifie généralement que les données sont lisibles ou que le tampon d'écriture est accessible en écriture). Les sélecteurs sont utilisés en Java pour implémenter le multiplexage.
Caractéristiques:
Avis:
Dans les applications pratiques, les E/S non bloquantes sont souvent utilisées en conjonction avec le multiplexage. Par exemple, un serveur peut utiliser un sélecteur pour surveiller plusieurs SocketChannels. Lorsqu'un SocketChannel contient des données pouvant être lues ou écrites, le sélecteur en informera le serveur et celui-ci traitera ce SocketChannel spécifique de manière non bloquante.
En Java, Stream et Channel sont deux manières différentes de traiter les flux de données. Ils appartiennent respectivement à la bibliothèque IO standard de Java et à la bibliothèque NIO. Voici une comparaison détaillée des deux concepts :
Stream fait partie du modèle IO (blocage IO) standard de Java, qui fournit un moyen de lire et d'écrire des données de manière séquentielle. Stream est divisé en deux types : InputStream et OutputStream, qui sont utilisés respectivement pour lire et écrire des données.Ces flux peuvent être des flux d'octets (tels queInputStream
, OutputStream
) ou un flux de caractères (tel queReader
, Writer
)。
Caractéristiques:
close()
méthode pour libérer des ressources.À partir de Java 7, l'instruction try-with-resources peut fermer automatiquement l'implémentationAutoCloseable
Ressources d’interface, y compris Stream. Channel fait partie du modèle Java NIO (New IO), qui offre un niveau d'abstraction plus élevé que Stream, permettant un traitement plus efficace des données. Les canaux peuvent être bidirectionnels, ce qui signifie qu'ils peuvent être utilisés pour lire et écrire des données.Les principaux types de canaux incluentFileChannel
、SocketChannel
、ServerSocketChannel
etDatagramChannel
。
Caractéristiques:
Résumer
En Java, les modèles d'opération d'E/S peuvent être classés en fonction des deux dimensions synchrone/asynchrone et bloquant/non bloquant.
définition : Le blocage synchrone des E/S est le modèle d'E/S le plus traditionnel lorsqu'un thread appelle une opération d'E/S (telle que la lecture ou l'écriture), le thread sera bloqué jusqu'à ce que l'opération soit terminée. Cela signifie que le thread ne peut effectuer aucune autre tâche tant que l’opération n’est pas terminée.
Caractéristiques:
InputStream
、OutputStream
、Socket
etServerSocket
scène.Exemple:
Utiliser le traditionnelInputStream
etOutputStream
Pour lire et écrire des fichiers :
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class SyncBlockingIOExample {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("input.txt");
FileOutputStream fos = new FileOutputStream("output.txt")) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
définition : Dans les E/S synchrones non bloquantes, le thread ne sera pas bloqué après l'appel de l'opération d'E/S. Si l'opération ne peut pas être terminée immédiatement, la méthode renverra immédiatement, renvoyant généralement une valeur spéciale (telle que -1 ou 0) ou lançant une exception pour indiquer que l'opération n'est pas terminée.
Caractéristiques:
Channels
etBuffers
, en appelantconfigureBlocking(false)
Réglez le canal en mode non bloquant.Exemple:
Utiliser NIOFileChannel
Pour une lecture et une écriture non bloquantes :
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.ByteBuffer;
public class SyncNonBlockingIOExample {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("input.txt");
FileOutputStream fos = new FileOutputStream("output.txt");
FileChannel inChannel = fis.getChannel();
FileChannel outChannel = fos.getChannel()) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
inChannel.configureBlocking(false);
while (inChannel.read(buffer) > 0) {
buffer.flip();
outChannel.write(buffer);
buffer.clear();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
définition: Utilisation du modèle d'E/S multiplexées synchronesSelector
(sélecteur) pour surveiller plusieursChannel
événement.Quand le fil appelleSelector.select()
, il bloquera jusqu'à ce qu'il y en ait au moins unChannel
Des événements (tels que lisible, inscriptible, demande de connexion, etc.) se produisent.
Caractéristiques:
Channel
, améliore la concurrence.Selector
etSelectionKey
Mécanisme capable de gérer efficacement un grand nombre de connexions simultanées.Exemple:
Utiliser NIOFileChannel
Pour une lecture et une écriture non bloquantes :
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Set;
public class SyncMultiplexingIOExample {
public static void main(String[] args) throws IOException {
try (Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
serverSocketChannel.socket().bind(new InetSocketAddress(8080));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
if (selector.select() > 0) {
Set<SelectionKey> keys = selector.selectedKeys();
for (SelectionKey key : keys) {
if (key.isAcceptable()) {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = ssc.accept();
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ);
}
}
keys.clear();
}
}
}
}
}
définition : Dans le modèle d'E/S asynchrone non bloquant, le thread revient immédiatement après le lancement de l'opération d'E/S sans attendre la fin de l'opération. Une fois l'opération terminée, le thread sera averti de manière asynchrone via une fonction de rappel ou une notification d'événement.
Caractéristiques:
AsynchronousChannel
Interface et implémentation de sa sous-classe, telle queAsynchronousFileChannel
etAsynchronousSocketChannel
。Exemple:
Utiliser NIOAsynchronousFileChannel
Effectuez la lecture et l'écriture de fichiers asynchrones :
import java.io.IOException;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
public class AsyncNonBlockingIOExample {
public static void main(String[] args) throws IOException, InterruptedException {
AsynchronousFileChannel inChannel = AsynchronousFileChannel.open(Paths.get("input.txt"), StandardOpenOption.READ);
AsynchronousFileChannel outChannel = AsynchronousFileChannel.open(Paths.get("output.txt"), StandardOpenOption.WRITE);
ByteBuffer buffer = ByteBuffer.allocate(1024);
CountDownLatch latch = new CountDownLatch(1);
inChannel.read(buffer, 0, buffer, 0, (channel, result) -> {
buffer.flip();
outChannel.write(buffer, 0, buffer, 0, null);
latch.countDown();
});
latch.await();
inChannel.close();
outChannel.close();
}
}
Remarque : Le modèle asynchrone nécessite la prise en charge du système d'exploitation sous-jacent (noyau).
définition : En théorie, ce modèle n'existe pas, car « asynchrone » signifie que l'opération est effectuée en arrière-plan et que le thread n'est pas bloqué. Par conséquent, le blocage asynchrone des E/S est un concept contradictoire et n'apparaîtra pas dans la pratique.
Le choix du modèle d'E/S approprié est essentiel pour la gestion des performances et des ressources. Dans les scénarios à forte concurrence, les modèles d'E/S synchrones non bloquantes ou d'E/S multiplexées synchrones sont des choix courants. Dans les scénarios qui nécessitent des performances et une vitesse de réponse extrêmes, le modèle d'E/S asynchrones non bloquantes est le premier choix.
Concept zéro copie
La technologie zéro copie signifie que pendant le processus de transmission de données d'un endroit à un autre, les données n'ont pas besoin d'être copiées entre l'espace utilisateur et l'espace noyau, ou du moins réduit le nombre de telles copies, améliorant ainsi l'efficacité du système. Dans les opérations d'E/S traditionnelles, lorsque les données sont lues à partir du réseau ou du disque, elles sont d'abord copiées dans le tampon de l'espace noyau, puis copiées de l'espace noyau vers le tampon de l'espace utilisateur. Au contraire, en version zéro copie, les données peuvent être traitées directement dans l'espace du noyau, ou directement transférées de l'espace du noyau vers le périphérique réseau, réduisant ainsi l'opération de copie du processeur, réduisant la surcharge du système et améliorant l'efficacité de la transmission des données.
Source de copie zéro
Le concept de copie zéro est apparu pour la première fois dans la conception des systèmes d'exploitation, dans le but de résoudre les goulots d'étranglement en termes de performances provoqués par la copie de données dans les opérations d'E/S traditionnelles. Dans les premiers systèmes informatiques, toutes les opérations d'E/S nécessitaient plusieurs copies de données dans l'espace utilisateur et dans l'espace noyau. Cela est progressivement devenu un goulot d'étranglement en termes de performances après que les réseaux à haut débit et les disques de grande capacité soient devenus courants.
Points techniques clés
Implémentation en Java :
Java prend en charge la technologie zéro copie via le framework NIO (New I/O). NIO présentéFileChannel
etSocketChannel
et d'autres classes, qui fournissent des opérations d'E/S plus efficaces. Spécifiquement,FileChannel.transferTo()
etFileChannel.transferFrom()
Les méthodes peuvent transférer des données directement d'un canal de fichier vers un canal de socket ou vice versa sans charger les données dans un tampon, obtenant ainsi une copie nulle.
Par exemple, supposons que vous deviez envoyer le contenu d'un fichier volumineux au réseau. L'approche traditionnelle consiste d'abord à lire le contenu du fichier dans un tampon, puis à écrire le contenu du tampon sur le réseau. Cela implique deux opérations de copie : une du disque vers le tampon et une autre du tampon vers le réseau.en utilisanttransferTo()
Lorsque vous utilisez cette méthode, les données peuvent être transférées directement du disque vers le réseau sans avoir besoin d'un tampon intermédiaire, ce qui réduit le nombre de copies et atteint zéro copie.
Un exemple utilisant ByteBuffer :
ByteBuffer
et autreBuffer
classe (commeCharBuffer
,ShortBuffer
etc.) fournissent des tampons qui peuvent être remplis ou vidés sans impliquer directement une copie entre l'espace utilisateur et l'espace noyau.ByteBuffer
Peut être utilisé directement ou indirectement dans la technologie zéro copie :
ByteBuffer.allocateDirect(size)
CrééByteBuffer
Les instances sont mappées directement sur la mémoire physique, en contournant le tas Java.Lorsqu'un tel tampon est comparé àFileChannel
ouSocketChannel
Lorsqu'elles sont utilisées ensemble, les données peuvent être transférées directement entre la mémoire physique et les périphériques matériels sans avoir besoin de copies supplémentaires via le tas Java. Cela permet un véritable zéro copie sur certaines plates-formes.FileChannel
detransferTo()
ettransferFrom()
: Ces méthodes permettent de stocker les données directement dansFileChannel
etSocketChannel
transmis entreByteBuffer
comme intermédiaire. Cela signifie que les données peuvent être transférées directement du disque au réseau ou vice versa sans avoir à les copier entre l'espace utilisateur et l'espace noyau.ByteBuffer
dewrap()
méthode:wrap()
La méthode vous permet d'envelopper un tableau d'octets existant ou un autre tampon dans unByteBuffer
, il n'est donc pas nécessaire de copier les données dans un nouveau tampon. Ceci est utile pour éviter la copie inutile des données.ByteBuffer
deslice()
méthode:slice()
La méthode crée une vue du tampon sans copier les données sous-jacentes.Cela signifie qu'un grandByteBuffer
Divisez en plusieurs tampons plus petits sans copier de données.Combinez ByteBuffer avec FileChannel :
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
public class ZeroCopyExample {
public static void main(String[] args) {
Path path = Path.of("example.txt");
try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
long transferred = fileChannel.transferTo(0, fileChannel.size(), System.out);
System.out.println("Transferred bytes: " + transferred);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Avis:
Nous devons noter qu'en fait, il n'existe pas de copie zéro complète. La copie zéro mentionnée ici concerne uniquement notre application elle-même, qui n'a pas de copie au niveau utilisateur. Mais même au niveau de l'utilisateur, il est impossible de n'avoir aucune opération de copie, mais de réduire les copies autant que possible. Par conséquent, nous pouvons comprendre que le terme zéro copie fait en réalité référence à la technologie de réduction du nombre de copies de données. et cela ne signifie pas qu'il n'y a pas d'opération de copie fidèle.