IO does not have much to do with multithreading, but NIO changes the way threads are used at the application level and solves some of the practical difficulties. AIO is also somewhat related to the asynchronous IO and the previous series. Here, in order to learn and record, also write an article to introduce NiO and AIO.
1. What is NiO
NIO is the abbreviation for new I/O, which, by name, represents a new set of Java I/O standards, relative to the legacy streaming I/O method. It is included in the JDK in Java 1.4 and has the following characteristics:
- NiO is based on block, which processes data in blocks (the units that are stored on the hard disk are also stored by block, which is better performance than the flow based approach).
- provides (buffer) caching support for all original types
- Increase the channel (Channel) object as a new raw I/O abstraction
- Support Locks (we often see a few. lock files when we use them, which means that threads are using this lock, and when the thread releases the lock, the file is removed so that other threads can continue to get the lock) and the file access interface of the memory-mapped file
- Provides asynchronous network I/O based on selector
All of the read and write operations from the channel have to go through the buffer, and the channel is the abstraction of Io, and the other end of the channel is the manipulated file.
2. Buffer
Implementation of buffer in Java. The basic data types all have its corresponding buffer.
Simple use examples of buffer:
Package test;
Import Java.io.File;
Import Java.io.FileInputStream;
Import Java.nio.ByteBuffer;
Import Java.nio.channels.FileChannel;
public class Test {public
static void Main (string[] args) throws Exception {
fileinputstream fin = new fileinputs Tream (New File (
"d:\\temp_buffer.tmp"));
FileChannel FC = Fin.getchannel ();
Bytebuffer Bytebuffer = bytebuffer.allocate (1024);
Fc.read (bytebuffer);
Fc.close ();
Bytebuffer.flip ()//read/write Conversion
}
}
The steps used in the summary are:
1. Get Channel
2. Apply for buffer
3. Establish the read/write relationship between channel and buffer
4. Close
The following example uses NIO to copy a file:
public static void Niocopyfile (string resource, string destination)
throws IOException {fileinputstream
fis = NE W FileInputStream (Resource);
FileOutputStream fos = new FileOutputStream (destination);
FileChannel Readchannel = Fis.getchannel (); Read File channel
filechannel writechannel = Fos.getchannel ()//write file channel
bytebuffer buffer = bytebuffer.allocate (1024); Read in data cache while
(true) {
buffer.clear ();
int len = readchannel.read (buffer); Read Data
if (len = = 1) {break
;//Read finished
}
buffer.flip ();
Writechannel.write (buffer); Write to File
}
readchannel.close ();
Writechannel.close ();
}
There are 3 important parameters in buffer: position (position), Capacity (capactiy), and upper bound (limit)
Here to distinguish between the capacity and the upper limit, such as a buffer of 10KB, then 10KB is the capacity, I will be 5KB file read into the buffer, then the upper limit is 5KB.
Here are a few examples to understand these 3 important parameters:
public static void Main (string[] args) throws Exception {
Bytebuffer b = bytebuffer.allocate (15);//15 byte size buffer
S Ystem.out.println ("limit=" + b.limit () + "capacity=" + b.capacity ()
+ "position=" + b.position ());
for (int i = 0; i < i++) {
//deposit 10 bytes of data
b.put ((byte) i);
}
System.out.println ("limit=" + b.limit () + "capacity=" + b.capacity ()
+ "position=" + b.position ());
B.flip (); Reset position
System.out.println ("limit=" + b.limit () + "capacity=" + b.capacity ()
+ "position=" + b.position ( ));
for (int i = 0; i < 5; i++) {
System.out.print (B.get ());
}
System.out.println ();
System.out.println ("limit=" + b.limit () + "capacity=" + b.capacity ()
+ "position=" + b.position ());
B.flip ();
System.out.println ("limit=" + b.limit () + "capacity=" + b.capacity ()
+ "position=" + b.position ());
The whole process is as follows:
At this point position from 0 to 10,capactiy and limit unchanged.
This action resets the position, which is typically required to perform this method when converting the buffer from write mode to read mode the flip () operation resets the current position to 0 and limit to the current position location.
The point of limit is to determine what data is meaningful, in other words, the data from position to limit is meaningful, because it's the last time the data was manipulated. So the flip operation is often the meaning of the read-write conversion.
Meaning ditto.
Most of the methods in buffer are to change these 3 parameters to achieve certain functions:
Public final Buffer Rewind ()
Set position to zero and clear the flag bit (mark)
Public final Buffer Clear ()
Set the position to zero while setting the limit to the capacity size and clear the flag mark
Public final Buffer Flip ()
Set the limit to the location of the position, then zero the position, and clear the flag mark, which is usually used when reading and writing conversion.
File mapped to memory
public static void Main (string[] args) throws Exception {
Randomaccessfile RAF = new Randomaccessfile ("C:\\mapfile.tx T "," RW ");
FileChannel FC = Raf.getchannel ();
Map files to memory
mappedbytebuffer MBB = Fc.map (FileChannel.MapMode.READ_WRITE, 0,
raf.length ());
while (Mbb.hasremaining ()) {
System.out.print ((char) mbb.get ());
}
Mbb.put (0, (byte) 98); Modify File
raf.close ();
}
Modifying the Mappedbytebuffer is equivalent to modifying the file itself, which is fast.
3. Channel
The general structure of multithreaded network servers:
Simple Multithreaded Server:
public static void Main (string[] args) throws Exception {
ServerSocket echoserver = null;
Socket clientsocket = null;
try {
echoserver = new ServerSocket (8000);
} catch (IOException e) {
System.out.println (e);
}
while (true) {
try {
clientsocket = echoserver.accept ();
System.out.println (clientsocket.getremotesocketaddress ()
+ "connect!");
Tp.execute (New Handlemsg (Clientsocket));
} catch (IOException e) {
System.out.println (e);
}
}
}
function is the server-side read what data, write back to the client what data.
The TP here is a thread pool, handlemsg is the class that handles messages.
Static class Handlemsg implements runnable{
omitted partial information public
void Run () {
try {is
= new BufferedReader (New I Nputstreamreader (Clientsocket.getinputstream ()));
OS = new PrintWriter (Clientsocket.getoutputstream (), true);
Reads the data sent by the client from the InputStream
String inputline = null;
Long B=system. Currenttimemillis ();
while ((Inputline = Is.readline ())!= null)
{
os.println (inputline);
}
Long E=system. Currenttimemillis ();
System. Out.println ("Spend:" + (E-b) + "MS");
} catch (IOException e) {
e.printstacktrace ();
} Finally
{
closing resource
}
}
}
Client:
public static void Main (string[] args) throws Exception {
Socket client = null;
PrintWriter writer = null;
BufferedReader reader = null;
try {
client = new Socket ();
Client.connect (New inetsocketaddress ("localhost", 8000));
writer = new PrintWriter (Client.getoutputstream (), true);
Writer.println ("hello!");
Writer.flush ();
reader = new BufferedReader (new InputStreamReader (
Client.getinputstream ()));
SYSTEM.OUT.PRINTLN ("From server:" + reader.readline ());
} catch (Exception e) {
} finally {
//omit resource close
}
}
The above network programming is very basic, use this way, there will be some problems:
Use a thread for each client, and if the client has an exception such as latency, the thread may be occupied for a long time. Because the data is prepared and read in this thread. At this point, if you have a large number of clients, you may consume a large amount of system resources.
Solution:
Use non-blocking NiO (read data does not wait, data is ready to work again)
To reflect the efficient use of NIO.
This simulates an inefficient client to simulate latency due to the network:
private static Executorservice tp= Executors.newcachedthreadpool ();
private static final int sleep_time=1000*1000*1000;
public static class Echoclient implements runnable{public
void Run () {
try {
client = new Socket ();
Client.connect (New inetsocketaddress ("localhost", 8000));
writer = new PrintWriter (Client.getoutputstream (), true);
Writer.print ("H");
Locksupport.parknanos (sleep_time);
Writer.print ("E");
Locksupport.parknanos (sleep_time);
Writer.print ("L");
Locksupport.parknanos (sleep_time);
Writer.print ("L");
Locksupport.parknanos (sleep_time);
Writer.print ("O");
Locksupport.parknanos (sleep_time);
Writer.print ("!");
Locksupport.parknanos (sleep_time);
Writer.println ();
Writer.flush ();
} catch (Exception e)
{
}
}
}
Server-Side output:
Spend:6000ms
Spend:6000ms
Spend:6000ms
Spend:6001ms
Spend:6002ms
Spend:6002ms
Spend:6002ms
Spend:6002ms
Spend:6003ms
Spend:6003ms
Because
while ((Inputline = Is.readline ())!= null)
is blocked, so time is spent waiting.
What would you do if you used NIO to deal with this problem?
One of the great features of NiO is that you tell me when you're ready to get the data.
And channel is a bit like a stream, a channel can correspond to a file or a network socket.
Selector is a selector, it can select a channel, and then do something.
A thread can correspond to a selector, while a selector can poll multiple channel, and each channel corresponds to a socket.
When using NIO, a thread can poll multiple sockets, as opposed to a single socket on the previous thread.
When selector calls select (), it looks at whether the client has the data ready. When no data is ready, the Select () blocks. NiO is often said to be non-blocking, but there is a blockage if no data is prepared.
When the data is ready, when the Select () is called, a selectionkey,selectionkey is returned indicating that the data for some channel on a selector has been prepared.
The channel will be chosen only when the data is ready.
In this way NIO implements a thread to monitor multiple clients.
The client that just simulated the network delay will not affect the threads under NIO, because when a socket network is delayed, the data is not ready, selector does not select it, and other prepared clients are selected.
The difference between Selectnow () and select () is that Selectnow () is not blocked, and when no client is ready for the data, Selectnow () does not block, returns 0, and when the client is ready for the data, Selectnow () Returns the number of prepared clients.
Main code:
Package test;
Import java.net.InetAddress;
Import java.net.InetSocketAddress;
Import Java.net.Socket;
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.nio.channels.spi.AbstractSelector;
Import Java.nio.channels.spi.SelectorProvider;
Import Java.util.HashMap;
Import Java.util.Iterator;
Import java.util.LinkedList;
Import Java.util.Map;
Import Java.util.Set;
Import Java.util.concurrent.ExecutorService;
Import java.util.concurrent.Executors; public class Multithreadnioechoserver {public static map<socket, long> geym_time_stat = new Hashmap<socket, Lon
G> ();
Class Echoclient {private linkedlist<bytebuffer> outq;
Echoclient () {outq = new linkedlist<bytebuffer> ();
Public linkedlist<bytebuffer> Getoutputqueue () {return outq; } public void Enqueue (Bytebuffer bb) {Outq.addfirSt (BB);
} class Handlemsg implements Runnable {Selectionkey sk;
Bytebuffer BB;
Public handlemsg (Selectionkey sk, Bytebuffer BB) {super ();
this.sk = SK;
this.bb = BB; @Override public void Run () {//TODO auto-generated method stub echoclient echoclient = (echoclient) Sk.att
Achment ();
Echoclient.enqueue (BB); Sk.interestops (Selectionkey.op_read |
Selectionkey.op_write);
Selector.wakeup ();
} private Selector Selector;
Private Executorservice TP = Executors.newcachedthreadpool ();
private void StartServer () throws Exception {selector = Selectorprovider.provider (). Openselector ();
Serversocketchannel SSC = Serversocketchannel.open ();
Ssc.configureblocking (FALSE);
Inetsocketaddress isa = new inetsocketaddress (8000);
Ssc.socket (). bind (ISA);
Register interested in events, where the Accpet event is of interest selectionkey Acceptkey = Ssc.register (selector, selectionkey.op_accept); for (;;)
{Selector.select (); Set Readykeys = Selector.selectedkeys ();
Iterator i = Readykeys.iterator ();
Long e = 0;
while (I.hasnext ()) {Selectionkey SK = (Selectionkey) i.next ();
I.remove ();
if (sk.isacceptable ()) {doaccept (SK); else if (Sk.isvalid () && sk.isreadable ()) {if (!geym_time_stat.containskey (Socketchannel) sk. Cha Nnel ()). Socket ()) {Geym_time_stat.put ((Socketchannel) Sk.channel ()). Socket (), system.currenttime
Millis ());
Doread (SK);
else if (Sk.isvalid () && sk.iswritable ()) {dowrite (SK);
E = System.currenttimemillis ();
Long B = Geym_time_stat.remove ((socketchannel) SK. Channel ()). socket ());
System.out.println ("Spend:" + (E-b) + "MS"); '}} ' private void Dowrite (Selectionkey sk) {//TODO auto-generated method stub Socketchannel channel = (S
Ocketchannel) Sk.channel ();
Echoclient echoclient = (echoclient) sk.attachment (); Linkedlist<bytebuffer> OUTQ = Echoclient.getoutputqueue ();
Bytebuffer BB = Outq.getlast ();
try {int len = channel.write (BB);
if (len = = 1) {disconnect (SK);
Return
} if (bb.remaining () = = 0) {outq.removelast ();
} catch (Exception e) {//Todo:handle Exception disconnect (SK);
} if (outq.size () = = 0) {sk.interestops (selectionkey.op_read); } private void Doread (Selectionkey sk) {//TODO auto-generated method stub Socketchannel channel = (Socketchann
EL) Sk.channel ();
Bytebuffer BB = bytebuffer.allocate (8192);
int Len;
try {len = channel.read (BB);
if (Len < 0) {disconnect (SK);
Return
} catch (Exception e) {//Todo:handle Exception disconnect (SK);
Return
} bb.flip ();
Tp.execute (New handlemsg (SK, BB)); } private void Disconnect (Selectionkey sk) {//TODO auto-generated Method Stub//omit slightly dry close operation} private void DOACC EPT (Selectionkey SK) {//TODO auto-generated method stub Serversocketchannel Server = (serversocketchannel) Sk.channel ();
Socketchannel Clientchannel;
try {Clientchannel = server.accept ();
Clientchannel.configureblocking (FALSE);
Selectionkey Clientkey = Clientchannel.register (selector, selectionkey.op_read);
Echoclient echoclinet = new Echoclient ();
Clientkey.attach (echoclinet);
InetAddress clientaddress = Clientchannel.socket (). getinetaddress ();
SYSTEM.OUT.PRINTLN ("Accepted connection from" + clientaddress.gethostaddress ()); catch (Exception e) {//Todo:handle Exception}} public static void Main (string[] args) {//TODO Auto-gen
erated method Stub Multithreadnioechoserver echoserver = new Multithreadnioechoserver ();
try {echoserver.startserver ();
catch (Exception e) {//Todo:handle Exception}}}
The code is for reference only, and the main feature is to be interested in different events to do different things.
This time consumption is between 2ms and 11ms when using the deferred client that was previously simulated. Performance improvements are obvious.
Summarize:
1. NIO will prepare the data and then submit it to the application for processing, and the read/write process of the data is still done in the application thread, but the waiting time is stripped to a separate thread.
2. Save data preparation time (because selector can be reused)
5. AIO
Features of AIO:
1. Let me know when you're done reading.
2. Will not speed up Io, only after reading the notice
3. Use callback function to do business processing
The relevant code for AIO:
Asynchronousserversocketchannel
Server = Asynchronousserversocketchannel.open (). bind (New Inetsocketaddress (PORT));
Using the Accept method on the server
public abstract <A> Void Accept (A attachment,completionhandler<asynchronoussocketchannel,? Super A> handler);
Completionhandler for the callback interface, when there is a client accept, do handler things.
Sample code:
Server.accept (NULL, New Completionhandler<asynchronoussocketchannel, object> () {final Bytebuffer buffer =
Bytebuffer.allocate (1024); public void completed (Asynchronoussocketchannel result, Object attachment) {System.out.println (Thread.curren
TThread (). GetName ());
Future<integer> writeresult = null;
try {buffer.clear ();
Result.read (buffer). Get (MB, timeunit.seconds);
Buffer.flip ();
Writeresult = result.write (buffer); catch (Interruptedexception |
Executionexception e) {e.printstacktrace ();
catch (TimeoutException e) {e.printstacktrace ();
Finally {try {server.accept (null, this);
Writeresult.get ();
Result.close ();
catch (Exception e) {System.out.println (e.tostring ()); @Override public void failed (Throwable exc, Object attachment) {System.out.println ("FA
iled: "+ exc); }
});
Here future is used to achieve immediate return, please refer to the previous article on future
On the basis of understanding NIO, see AIO, the difference is that AIO is the completion of the read and write process and then call the callback function.
NiO is synchronous and non-blocking
AIO is asynchronous and non-blocking
Because the reading and writing process of NiO is still done in the application thread, NIO is not appropriate for those who read and write the process for a long time.
And Aio's read and write process is completed before being notified, so AIO can be competent for those heavyweight, read and write process long tasks.