preface

In daily development, IO is everywhere, but really do IO related development, should be relatively few, today we will understand

Synchronous/Asynchronous

Synchronization is when two or more things stay relative to each other over time. In the computer world, we can think of things as requests. In a synchronous scenario, requests must wait for a result to be returned before proceeding, and multiple requests must be executed one by one. Asynchronous, as opposed to synchronous, requests do not need to wait for a result to be returned before proceeding. Multiple requests can be executed simultaneously. For example, if you go to a restaurant to eat, in a synchronous scenario, after ordering a dish, you have to wait for the chef to wash, cook, and serve the dish before ordering the next one. In an asynchronous scenario, after you order a dish, the chef can go to work, and you can move on to the next dish

Blocking/non-blocking

Blocking means there is an obstacle that cannot be passed, and non-blocking means of course there is no obstacle that can be passed. We can use traffic jam to describe the blocking and non-blocking, blocked, the car is not moving, non-blocking, the car is active, in a thread, the state of static is the thread is suspended, active state is the thread is running

differences

At first glance, synchronous/asynchronous and blocking/non-blocking look similar, but they have different concerns. Synchronous/asynchronous is concerned with whether multiple requests can block at the same time. Non-blocking is concerned with the state of the thread when it executes

BIO

As shown above

BIO (Blocking I/O) : Synchronously blocks the IO. Every time a client connects to the server, the server starts a thread for processing. If the connection does nothing, this causes unnecessary thread overhead

Code sample
Public class BIOServer {public static void main(String[] args) throws IOException {// If there is a client connection, the server must start a thread. So using optimization of thread pool ExecutorService ExecutorService = Executors. NewCachedThreadPool (); ServerSocket serverSocket = new ServerSocket(6666); System.out.println(" server started "); While (true) {final Socket Socket = serverSocket.accept(); System.out.println(" connect to a client "); Execute (() -> handle(socket)); // Create an executorService.execute(() -> handle(socket)); }} public static void handle(Socket Socket) {system.out.println (" Thread information ID = "+ thread.currentThread ().getid () + ", Name = "+ thread.currentThread ().getName()); try { byte[] b = new byte[1024]; InputStream stream = socket.getInputStream(); while (true) { int read = stream.read(b); if (read ! = -1) { System.out.println(new String(b, 0, read)); } else { break; } } } catch (Exception e) { e.printStackTrace(); } finally { try { socket.close(); } catch (Exception e) { e.printStackTrace(); }}}}Copy the code
The problem
  1. Each client request needs to create a separate thread for processing. When the number of concurrent requests is large, the server needs to create a large number of threads, which occupies a large number of resources
  2. After the connection is established, if the current thread has no data to read, the thread will block the read operation, resulting in a waste of resources

NIO

Because of BIO’s problems, Java has made a series of improvements, called NIO, that are synchronous and non-blocking

Let’s look at NIO as a whole

  1. NIO has three cores: Selector, Channel, and Buffer. Each Channel corresponds to a Buffer. Multiple channels can be registered in the same Selector, and one Selector corresponds to one thread
  2. NIO is Buffer oriented programming, so you can read and write the same Buffer, unlike BIO, which is stream oriented programming, either input stream or output stream
  3. NIO is non-blocking, a thread can process multiple channels, the current channel does not have read and write operations, does not block on the current channel, but will process the channels that have read and write operations
Buffer

The underlying Buffer is an array. Within this array, there are three particularly important variables: position, LIMIT, and Capacity

  1. Position: Indicates the index of the next element to be written or read
  2. Limit: points to the end of the buffer,
  3. Capacity: indicates the maximum capacity of the buffer

When creating a Buffer, the position, limit, and capacity Pointers point to the following

When an element is added to the Buffer, the position, limit, and capacity Pointers point to the following

When the fifth element is added to the Buffer, the position, limit, and capacity Pointers point to the following

When we want to read from the buffer, we need to flip the buffer. After the flip, the position, limit, and Capacity Pointers point as follows

The sample code
public class BufferTest { public static void main(String[] args) { IntBuffer intBuffer = IntBuffer.allocate(10); for (int i = 0; i < 10; i++) { int temp = new Random().nextInt(100); System.out.println(temp); intBuffer.put(temp); } // Flip the Buffer intbuffer.flip (); System.out.println("-------------------"); while (intBuffer.hasRemaining()) { System.out.println(intBuffer.get()); }}}Copy the code
The source code

The common Buffer subclass, which represents the data type stored, is easy to understand

ByteBuffer, IntBuffer, LongBuffer, ShortBuffer, CharBuffer, DoubleBuffer, FloatBuffer

When we use intBuffer.allocate (10), the source code is as follows

public static IntBuffer allocate(int capacity) { if (capacity < 0) throw new IllegalArgumentException(); Return new HeapIntBuffer(capacity, capacity); // HeapIntBuffer is an abstract class. }Copy the code
Public final Buffer flip() {limit = position; position = 0; mark = -1; return this; }Copy the code
Channel

A Channel is similar to a stream, but not quite the same as a stream. A stream is a one-way flow of water that flows upstream. A FileInputStream can only be read, but a Channel is a two-way flow

public class ChannelTest { public static void main(String[] args) throws IOException { FileInputStream fileInputStream =  new FileInputStream("input.txt"); FileChannel inputStreamChannel = fileInputStream.getChannel(); FileOutputStream fileOutputStream = new FileOutputStream("output.txt"); FileChannel outputStreamChannel = fileOutputStream.getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocate(64); While (true) {// reset the array pointer. This is very important. Bytebuffer.clear (); int read = inputStreamChannel.read(byteBuffer); If (read == -1) {break; } byteBuffer.flip(); outputStreamChannel.write(byteBuffer); } inputStreamChannel.close(); outputStreamChannel.close(); }}Copy the code
Three major components are used together

The Selector, Channel, and Buffer components are used together

public class NIOServer { public static void main(String[] args) throws IOException { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); Selector selector = Selector.open(); / / listen on port 6666 serverSocketChannel. Socket (), bind (new InetSocketAddress (6666)); serverSocketChannel.configureBlocking(false); / / channel registration to the selector, concerned about the events for OP_ACCEPT serverSocketChannel. Register (selector, SelectionKey. OP_ACCEPT); While (true) {if (selector. Select (1000) == 0) {system.out.println (" server wait 1 second, no connection "); continue; Set<SelectionKey> selectionKeys = selection.selectedKeys (); selectionKeys = selection.selectedKeys (); Iterator<SelectionKey> iterator = selectionKeys.iterator(); While (iterator.hasnext ()) {SelectionKey SelectionKey = iterator.next(); // If there is something that is acceptable, That there is a new client connection if (selectionKey isAcceptable ()) {/ / the client generates a SocketChannel SocketChannel SocketChannel = serverSocketChannel.accept(); System.out.println(" Client connected successfully, socketChannel was generated, socketChannel: "+ SocketChannel.hashcode ()); socketChannel.configureBlocking(false); Socketchannel. register(Selector, SelectionKey.op_read, byteBuffer.allocate (1024)); } if (selectionkey.isreadable ()) {SocketChannel Channel = (SocketChannel) selectionKey.channel(); ByteBuffer buffer = (ByteBuffer) selectionKey.attachment(); channel.read(buffer); System.out.println("form client "+ new String(buffer.array())); } iterator.remove(); }}}}Copy the code

The resources

zhuanlan.zhihu.com/p/66148226