I'm quite fond of Java NIO and I really want to apply Java NIO to my current system, but when I created these samples application to compare between Java IO and NIO, it made me quite disappointed.
Here are my 2 samples (I don't put all the source code)
Java IO
public class BlockingServerClient {
private static final Logger log = Logger.getLogger(BlockingServerClient.class.getName());
static final ExecutorService service = Executors.newCachedThreadPool();
public static void main(String[] args) throws InterruptedException {
int port = Integer.parseInt(args[0]);
BlockingServerClient server = new BlockingServerClient();
Server sr = server.new Server(port);
service.submit(sr);
}
private class Server implements Runnable {
.....
public void run() {
ServerSocket ss = null;
try {
ss = new ServerSocket(localPort);
log.info("Server socket bound to " + localPort);
while (true) {
Socket client = ss.accept();
log.info("Accepted connection from " + client.getRemoteSocketAddress());
service.submit(new SocketClient(client));
}
} catch (IOException e) {
log.log(Level.SEVERE, "Server error", e);
} finally {
.....
}
}
}
private class SocketClient implements Runnable {
.....
public void run() {
InetSocketAddress addr = (InetSocketAddress) socket.getRemoteSocketAddress();
socketInfo = String.format("%s:%s", addr.getHostName(), addr.getPort());
log.info("Start reading data from " + socketInfo);
try {
in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
String input;
while ((input = in.readLine()) != null) {
log.info(String.format("[%s] %s", socketInfo, input));
log.info("Socket " + socketInfo + " thread sleep 4s");
TimeUnit.SECONDS.sleep(4);
}
} catch (Exception ex) {
log.log(Level.SEVERE, "Socket error", ex);
} finally {
.....
}
}
}
}
Java NIO
public class NonBlockingServerClient {
private static final Logger log = Logger.getLogger(NonBlockingServerClient.class.getName());
public static void main(String[] args) {
int port = Integer.parseInt(args[0]);
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try {
NonBlockingServerClient sc = new NonBlockingServerClient();
Server server = sc.new Server(port, boss, worker);
server.run();
} catch (Exception e) {
log.log(Level.SEVERE, "Error", e);
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
private class Server {
.....
public void run() {
log.info("Start Server bootstrap");
ServerBootstrap b = new ServerBootstrap();
b.group(boss, worker)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipe = ch.pipeline();
pipe.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipe.addLast(new StringDecoder());
pipe.addLast(new ClientHandler());
}
});
ChannelFuture future = null;
try {
future = b.bind(port).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
log.log(Level.SEVERE, "Server binding error", e);
future.channel().close();
}
}
}
private class ClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg)
throws Exception {
log.info(String.format("[%s] %s", ctx.channel().remoteAddress(), msg));
log.info(ctx.channel().remoteAddress() + " sleep 4s");
TimeUnit.SECONDS.sleep(4);
}
}
}
Client
public class Client {
private static final Logger log = Logger.getLogger(Client.class.getName());
public static void main(String[] args) throws InterruptedException {
int port = Integer.parseInt(args[0]);
for (int i = 0; i < 10; i++) {
Client cl = new Client("localhost", port);
cl.start();
TimeUnit.MILLISECONDS.sleep(500);
}
}
String host;
int port;
public Client(String host, int port) {
this.host = host;
this.port =port;
}
public void start() {
log.info("Start client running");
Socket socket = null;
String info = "";
try {
socket = new Socket(host, port);
InetSocketAddress addr = (InetSocketAddress) socket.getLocalSocketAddress();
info = String.format("%s:%s", addr.getHostName(), addr.getPort());
int count = 10;
OutputStream out = socket.getOutputStream();
while (count > 0) {
String outStr = "Output-" + count + "\n";
out.write(outStr.getBytes());
out.flush();
count--;
}
out.write((info + "-Finish sending").getBytes());
out.flush();
} catch (Exception e) {
log.log(Level.SEVERE, "Client error", e);
} finally {
try {
socket.close();
log.info(info + "-Client close");
} catch (IOException e) {
log.log(Level.SEVERE, "Closing client error", e);
}
}
}
}
The Client when run will create 10 clients connect to the server. After I run and monitored several times, I found no different between Java IO and NIO.
If changed number of clients to 500, I found that the java IO really created 500 threads, however the consumption of data really fast. In contrast, the java NIO application had much fewer threads than the other, but the consumption of data was slow and it took longer time to finish everything.
So, what is really the benefit of Java NIO? Create fewer threads to save memory but slower in performance.
Or, I might did it wrongly.
util. *; imports all of those classes. java.io contains things like FileReader, InputStream, OutputStream etc.
Provides for system input and output through data streams, serialization and the file system. Provides classes that are fundamental to the design of the Java programming language. Defines buffers, which are containers for data, and provides an overview of the other NIO packages.
nio libraries allow for non-blocking communication using Buffers and Channels and can provide direct memory access for faster performance.
The Java NIO (New Input/Output) API defines buffers, which are containers for data, and other structures, such as charsets, channels, and selectable channels. Charsets are mappings between bytes and Unicode characters. Channels represent connections to entities capable of performing I/O operations.
The speed difference that you are noticing is because of the 4s sleep that appears in both test cases.
In the non-NIO case, which has one thread per request, sleeping for 4 seconds only blocks that one request. However in the NIO case, which has a far smaller number of worker threads, it blocks that request and every other request that is waiting to run on that thread.
So this begs the question, why would we want to use less threads with the NIO approach? And the answer is scalability. Modern Operating Systems have a scaling problem related to the number of threads blocked on network IO. See the C10K problem for more details.
In general I do find NIO to be faster, or at least it has the potential to be much faster if:
This is where async frameworks like akka shine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With