minhas informações de contato
Correspondênciamesophia@protonmail.com
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Descreve o conhecimento básico do NIO no nível ava, para revisão básica
Em Java, NIO (E/S sem bloqueio ou Nova E/S) é um novo conjunto de API de operação de entrada/saída introduzido no Java SE 1.4 e versões subsequentes.
Em comparação com o modelo IO tradicional, oferece maior eficiência e melhores capacidades de processamento simultâneo. O principal recurso do NIO é seu recurso sem bloqueio, que permite que um único thread gerencie vários canais de E/S, melhorando bastante o desempenho do aplicativo em cenários de alta simultaneidade.
O cenário de uso do Java NIO é particularmente adequado para situações em que um grande número de conexões simultâneas precisa ser processado. Por exemplo, em um servidor de rede, um thread pode gerenciar milhares de conexões de clientes sem a necessidade de alocar um thread independente para cada conexão. . Pode reduzir significativamente o consumo de recursos do sistema e melhorar as capacidades de processamento.
Canal é o meio de fluxo de dados em Java NIO. É bidirecional e pode ser usado para ler ou gravar dados. A principal vantagem dos canais é que eles não são bloqueadores, o que significa que um thread pode gerenciar vários canais. Quando nenhum evento ocorre em um canal, o thread não será bloqueado e poderá, em vez disso, lidar com outras tarefas. Os principais tipos de canais incluem:
FileChannel
: Usado para operações de leitura e gravação de arquivo. Pode ser usado para gravar dados do buffer no arquivo ou para ler dados do arquivo no buffer.SocketChannel
: Usado para conexões TCP em comunicações de rede e pode ser usado para ler e gravar dados.ServerSocketChannel
: Usado para aceitar novas conexões SocketChannel, semelhantes ao ServerSocket tradicional.DatagramChannel
: Usado para comunicação UDP, que pode enviar e receber datagramas.Buffer é um objeto usado para armazenar dados em NIO. É uma matriz de bytes que pode gravar e ler dados dele. Um buffer tem uma capacidade específica e duas propriedades importantes: posição e limite.
Os principais tipos de buffers sãoByteBuffer
、CharBuffer
、ShortBuffer
、IntBuffer
、LongBuffer
、FloatBuffer
eDoubleBuffer
, cada tipo corresponde a um tipo de dados primitivo.
Um seletor é um multiplexador em NIO que permite que um único thread monitore eventos de vários canais, como leitura, gravação, conexão e recebimento de eventos. O seletor notifica a aplicação quando um dos canais está pronto para operações de E/S. O uso de seletores melhora muito a capacidade de processamento simultâneo de aplicativos de rede porque não há necessidade de criar um thread para cada conexão.
Os principais métodos de seletores incluem:
select()
: Bloqueia até que pelo menos um canal esteja pronto para operações de E/S.selectedKeys()
: Retorna um Conjunto contendo os objetos SelectionKey de todos os canais preparados.wakeup()
: Interrupção bloqueadaselect()
transferir.SelectionKey
É a associação entre seletores e canais. Representa o status de registro de um canal no seletor, incluindo canais, seletores, coleções de eventos interessados e coleções de eventos prontos.
Este é o modelo de E/S mais tradicional em Java, as APIs Socket e ServerSocket tradicionais são baseadas no bloqueio de E/S. Neste modo, quando um thread inicia uma operação de leitura ou gravação, o thread é bloqueado até que a operação de E/S seja concluída. Se não houver dados para ler para a operação de leitura ou se a operação de gravação não puder ser concluída imediatamente, o thread aguardará até que a operação seja concluída.
Características:
A E/S sem bloqueio faz parte da estrutura Java NIO (New I/O), que permite que threads iniciem operações de leitura e gravação sem serem bloqueadas. Se não houver dados para ler para uma operação de leitura ou se a operação de gravação não puder ser concluída imediatamente, o thread não será suspenso e poderá continuar a executar outras tarefas.
Características:
Multiplexação é monitorar vários descritores de arquivo ao mesmo tempo por meio de um thread e operar apenas em um descritor quando ele estiver pronto (geralmente significa que os dados são legíveis ou o buffer de gravação é gravável). Seletores são usados em Java para implementar multiplexação.
Características:
Perceber:
Em aplicações práticas, a E/S sem bloqueio é frequentemente usada em conjunto com a multiplexação. Por exemplo, um servidor pode usar um Seletor para monitorar vários SocketChannels. Quando um SocketChannel possui dados que podem ser lidos ou gravados, o Seletor notificará o servidor e o servidor processará esse SocketChannel específico de maneira sem bloqueio.
Em Java, Stream e Channel são duas maneiras diferentes de processar fluxos de dados. Eles pertencem à biblioteca IO padrão e à biblioteca NIO do Java, respectivamente. Aqui está uma comparação detalhada dos dois conceitos:
Stream faz parte do modelo IO (IO de bloqueio) padrão do Java, que fornece uma maneira de ler e gravar dados sequencialmente. Stream é dividido em dois tipos: InputStream e OutputStream, que são usados para ler e gravar dados respectivamente.Esses fluxos podem ser fluxos de bytes (comoInputStream
, OutputStream
) ou um fluxo de caracteres (comoReader
, Writer
)。
Características:
close()
método para liberar recursos.A partir do Java 7, a instrução try-with-resources pode fechar automaticamente a implementaçãoAutoCloseable
Recursos de interface, incluindo Stream. Channel faz parte do modelo Java NIO (New IO), que fornece um nível de abstração superior ao Stream, permitindo um processamento de dados mais eficiente. Os canais podem ser bidirecionais, o que significa que podem ser usados para ler e gravar dados.Os principais tipos de canais incluemFileChannel
、SocketChannel
、ServerSocketChannel
eDatagramChannel
。
Características:
Resumir
Em Java, os modelos de operação de E/S podem ser classificados com base nas duas dimensões de síncrono/assíncrono e bloqueador/não bloqueador.
definição : O bloqueio síncrono de E/S é o modelo de E/S mais tradicional. Quando um thread chama uma operação de E/S (como leitura ou gravação), o thread será bloqueado até que a operação seja concluída. Isso significa que o thread não pode executar nenhuma outra tarefa até que a operação seja concluída.
Características:
InputStream
、OutputStream
、Socket
eServerSocket
cena.Exemplo:
Usar tradicionalInputStream
eOutputStream
Para ler e gravar arquivos:
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();
}
}
}
definição : Na E/S síncrona sem bloqueio, o thread não será bloqueado após chamar a operação de E/S. Se a operação não puder ser concluída imediatamente, o método retornará imediatamente, geralmente retornando um valor especial (como -1 ou 0) ou lançando uma exceção para indicar que a operação não foi concluída.
Características:
Channels
eBuffers
, ligandoconfigureBlocking(false)
Defina o canal para o modo sem bloqueio.Exemplo:
Usando NIOFileChannel
Para leitura e escrita sem bloqueio:
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();
}
}
}
definição: Uso do modelo de E/S multiplexado síncronoSelector
(seletor) para monitorar múltiplosChannel
evento.Quando o thread chamaSelector.select()
, ele será bloqueado até que haja pelo menos umChannel
Ocorrem eventos (como legível, gravável, solicitação de conexão, etc.).
Características:
Channel
, melhora a simultaneidade.Selector
eSelectionKey
Mecanismo que pode lidar com eficiência com um grande número de conexões simultâneas.Exemplo:
Usando NIOFileChannel
Para leitura e escrita sem bloqueio:
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();
}
}
}
}
}
definição : No modelo de E/S assíncrono sem bloqueio, o thread retorna imediatamente após iniciar a operação de E/S sem esperar que a operação seja concluída. Quando a operação for concluída, o thread será notificado de forma assíncrona por meio de uma função de retorno de chamada ou notificação de evento.
Características:
AsynchronousChannel
Interface e sua implementação de subclasse, comoAsynchronousFileChannel
eAsynchronousSocketChannel
。Exemplo:
Usando NIOAsynchronousFileChannel
Execute leitura e gravação assíncrona de arquivos:
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();
}
}
Nota: O modelo assíncrono requer suporte do sistema operacional subjacente (Kernel)
definição : Em teoria, esse modelo não existe, pois "assíncrono" significa que a operação é executada em segundo plano e o thread não é bloqueado. Portanto, o bloqueio assíncrono de E/S é um conceito contraditório e não aparecerá na prática.
A escolha do modelo de E/S apropriado é fundamental para o desempenho e o gerenciamento de recursos. Em cenários de alta simultaneidade, modelos de E/S síncrona sem bloqueio ou de E/S síncrona multiplexada são escolhas comuns. Em cenários que exigem desempenho e velocidade de resposta extremos, o modelo de E/S assíncrona sem bloqueio é a primeira escolha.
Conceito de cópia zero
A tecnologia de cópia zero significa que durante o processo de transmissão de dados de um lugar para outro, os dados não precisam ser copiados entre o espaço do usuário e o espaço do kernel, ou pelo menos reduz o número dessas cópias, melhorando assim a eficiência do sistema. Nas operações tradicionais de E/S, quando os dados são lidos da rede ou do disco, eles são primeiro copiados para o buffer no espaço do kernel e depois copiados do espaço do kernel para o buffer no espaço do usuário. Pelo contrário, na cópia zero, os dados podem ser processados diretamente no espaço do kernel ou transferidos diretamente do espaço do kernel para o dispositivo de rede, reduzindo assim a operação de cópia da CPU, reduzindo a sobrecarga do sistema e melhorando a eficiência da transmissão de dados.
Fonte de cópia zero
O conceito de cópia zero apareceu pela primeira vez no projeto de sistemas operacionais, com o objetivo de resolver o gargalo de desempenho causado pela cópia de dados em operações tradicionais de E/S. Nos primeiros sistemas de computador, todas as operações de E/S exigiam múltiplas cópias de dados no espaço do usuário e no espaço do kernel. Isso gradualmente se tornou um gargalo de desempenho depois que redes de alta velocidade e discos de grande capacidade se tornaram comuns.
Principais pontos técnicos
Implementação em Java:
Java oferece suporte à tecnologia de cópia zero por meio da estrutura NIO (New I/O). NIO introduzidoFileChannel
eSocketChannel
e outras classes, que fornecem operações de E/S mais eficientes. Especificamente,FileChannel.transferTo()
eFileChannel.transferFrom()
Os métodos podem transferir dados diretamente de um canal de arquivo para um canal de soquete ou vice-versa, sem carregar os dados em um buffer, obtendo assim zero cópia.
Por exemplo, suponha que você precise enviar o conteúdo de um arquivo grande para a rede. A abordagem tradicional é primeiro ler o conteúdo do arquivo em um buffer e depois gravá-lo na rede. Isto envolve duas operações de cópia: uma do disco para o buffer e outra do buffer para a rede.Enquanto estiver usandotransferTo()
Ao utilizar este método, os dados podem ser transferidos diretamente do disco para a rede sem a necessidade de um buffer intermediário, o que reduz o número de cópias e atinge zero cópia.
Um exemplo usando ByteBuffer:
ByteBuffer
e outroBuffer
classe (comoCharBuffer
,ShortBuffer
etc.) fornecem buffers que podem ser preenchidos ou esvaziados sem envolver diretamente uma cópia entre o espaço do usuário e o espaço do kernel.ByteBuffer
Pode ser usado direta ou indiretamente na tecnologia de cópia zero:
ByteBuffer.allocateDirect(size)
CriadaByteBuffer
As instâncias são mapeadas diretamente para a memória física, ignorando o heap Java.Quando tal buffer é comparado comFileChannel
ouSocketChannel
Quando usados em conjunto, os dados podem ser transferidos diretamente entre a memória física e os dispositivos de hardware sem a necessidade de cópias adicionais por meio do heap Java. Isso permite cópia zero verdadeira em algumas plataformas.FileChannel
detransferTo()
etransferFrom()
: Esses métodos permitem que os dados sejam armazenados diretamente emFileChannel
eSocketChannel
transmitido entreByteBuffer
como intermediário. Isso significa que os dados podem ser transferidos diretamente do disco para a rede ou vice-versa, sem a necessidade de serem copiados entre o espaço do usuário e o espaço do kernel.ByteBuffer
dewrap()
método:wrap()
método permite agrupar uma matriz de bytes existente ou outro buffer em umByteBuffer
, portanto não há necessidade de copiar os dados para um novo buffer. Isso é útil para evitar cópias desnecessárias de dados.ByteBuffer
deslice()
método:slice()
O método cria uma visualização do buffer sem copiar os dados subjacentes.Isto significa que um grandeByteBuffer
Divida em vários buffers menores sem copiar dados.Use ByteBuffer com 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();
}
}
}
Perceber:
Devemos observar que, na verdade, não existe uma cópia zero completa. A cópia zero mencionada aqui é apenas para o nosso aplicativo em si, que não possui uma cópia no nível do usuário. Mas mesmo no nível do usuário, é impossível não ter nenhuma operação de cópia, mas sim reduzir ao máximo as cópias. Portanto, podemos entender que o termo cópia zero na verdade se refere à tecnologia de redução do número de cópias de dados. e isso não significa que não haja operação de cópia verdadeira.