Java Nio 简明教程

Java NIO - Quick Guide

Java NIO - Overview

java.nio 包是在 java 1.4 中引入的。与 java I/O 相比,java NIO 中引入了面向缓冲区和通道的数据流进行 I/O 操作,这最终提供了更快的执行速度和更好的性能。

Java.nio package was introduced in java 1.4. In contrast of java I/O in java NIO the buffer and channel oriented data flow for I/O operations is introduced which in result provide faster execution and better performance.

NIO API 还提供了选择器,它引入了以异步或非阻塞方式侦听多个通道的 I/O 事件的功能。在 NIO 中,包括将缓冲区填充和清空到操作系统等最耗时的 I/O 活动在内的速度都得到了提升。

Also NIO API offer selectors which introduces the functionality of listen to multiple channels for IO events in asynchronous or non blocking way.In NIO the most time-consuming I/O activities including filling and draining of buffers to the operating system which increases in speed.

NIO API 的核心抽象如下:

The central abstractions of the NIO APIs are following −

  1. Buffers,which are containers for data,charsets and their associated decoders and encoders,which translate between bytes and Unicode characters.

  2. Channels of various types,which represent connections to entities capable of performing I/O operations

  3. Selectors and selection keys, which together with selectable channels define a multiplexed, non-blocking I/O facility.

Java NIO - Environment Setup

本部分指导您如何在计算机上下载和设置 Java。请按照以下步骤设置环境。

This section guides you on how to download and set up Java on your machine. Please follow the following steps to set up the environment.

Java SE 可以从链接 Download Java 中免费获取。因此,您可以根据自己的操作系统下载版本。

Java SE is freely available from the link Download Java. So you download a version based on your operating system.

按照说明下载 Java 并运行 .exe 在您的机器上安装 Java。在计算机上安装 Java 后,您需要设置环境变量以指向正确的安装目录 −

Follow the instructions to download java and run the .exe to install Java on your machine. Once you installed Java on your machine, you would need to set environment variables to point to correct installation directories −

Setting up the path for windows 2000/XP

假设你已将 Java 安装在 c:\Program Files\java\jdk 目录中 −

Assuming you have installed Java in c:\Program Files\java\jdk directory −

  1. Right-click on 'My Computer' and select 'Properties'.

  2. Click on the 'Environment variables' button under the 'Advanced' tab.

  3. Now alter the 'Path' variable so that it also contains the path to the Java executable. Example, if the path is currently set to 'C:\WINDOWS\SYSTEM32', then change your path to read 'C:\WINDOWS\SYSTEM32;c:\Program Files\java\jdk\bin'.

Setting up the path for windows 95/98/ME

假设你已将 Java 安装在 c:\Program Files\java\jdk 目录中 −

Assuming you have installed Java in c:\Program Files\java\jdk directory −

  1. Edit the 'C:\autoexec.bat' file and add the following line at the end: 'SET PATH = %PATH%;C:\Program Files\java\jdk\bin'

Setting up the path for Linux, UNIX, Solaris, FreeBSD

环境变量 PATH 应设置为指向已安装 Java 二进制文件的位置。如果您在执行此操作时遇到问题,请参阅您的 shell 文档。

Environment variable PATH should be set to point to where the java binaries have been installed. Refer to your shell documentation if you have trouble doing this.

例如,如果您使用 bash 作为您的 shell,那么您将向 '.bashrc 的尾部添加以下行: export PATH = /path/to/java:$PATH'

Example, if you use bash as your shell, then you would add the following line to the end of your '.bashrc: export PATH = /path/to/java:$PATH'

要编写 Java 程序,您需要一个文本编辑器。市场上还有更多高级的 IDE 可用。但现在,您可以考虑以下选项之一 −

To write your java programs you will need a text editor. There are even more sophisticated IDE available in the market. But for now, you can consider one of the following −

  1. Notepad − On Windows machine you can use any simple text editor like Notepad (Recommended for this tutorial), TextPad.

  2. Netbeans − is a Java IDE that is open source and free which can be downloaded from http://www.netbeans.org/index.html.

  3. Eclipse − is also a java IDE developed by the eclipse open source community and can be downloaded from https://www.eclipse.org/.

Java NIO vs IO

正如我们所知,Java NIO 引入是为了提升常规 Java IO API。NIO 比 IO 更有效率的主要改进是 NIO 中使用的数据流模型,以及使用操作系统执行常规 IO 任务。

As we know that java NIO is introduced for advancement of conventional java IO API.The main enhancements which make NIO more efficient than IO are channel data flow model used in NIO and use of operating system for conventional IO tasks.

Java NIO 和 Java IO 之间的差异可以解释如下 −

The difference between Java NIO and Java IO can be explained as following −

  1. As mentioned in previous post in NIO buffer and channel oriented data flow for I/O operations which provide faster execution and better performance as compare to IO.Also NIO uses operating system for conventional I/O tasks which again makes it more efficient.

  2. Other aspect of difference between NIO and IO is this IO uses stream line data flow i.e one more byte at a time and relies on converting data objects into bytes and vice-e-versa while NIO deals with the data blocks which are chunks of bytes.

  3. In java IO stream objects are unidirectional while in NIO channels are bidirectional meaning a channel can be used for both reading and writing data.

  4. The streamline data flow in IO does not allow move forth and back in the data.If case need to move forth and back in the data read from a stream need to cache it in a buffer first.While in case of NIO we uses buffer oriented which allows to access data back and forth without need of caching.

  5. NIO API also supports multi threading so that data can be read and written asynchronously in such as a way that while performing IO operations current thread is not blocked.This again make it more efficient than conventional java IO API.

  6. Concept of multi threading is introduced with the introduction of Selectors in java NIO which allow to listen to multiple channels for IO events in asynchronous or non blocking way.

  7. Multi threading in NIO make it Non blocking which means that thread is requested to read or write only when data is available otherwise thread can be used in other task for mean time.But this is not possible in case of conventional java IO as no multi threading is supported in it which make it as Blocking.

  8. NIO allows to manage multiple channels using only a single thread,but the cost is that parsing the data might be somewhat more complicated than when reading data from a blocking stream in case of java IO.So in case fewer connections with very high bandwidth are required with sending a lot of data at a time,than in this case java IO API might be the best fit.

Java NIO - Channels

Description

顾名思义,管道用作从一端到另一端的数据流方式。此处在 Java NIO 中,管道在缓冲区和另一端的一个实体之间起着同样的作用,换句话说,管道用于将数据读入缓冲区并从缓冲区写入数据。

As name suggests channel is used as mean of data flow from one end to other.Here in java NIO channel act same between buffer and an entity at other end in other words channel are use to read data to buffer and also write data from buffer.

与在传统 Java IO 中使用的流不同,管道是双向的,即既可以读取又可以写入。Java NIO 管道既支持阻塞模式,也支持非阻塞模式下的异步数据流。

Unlike from streams which are used in conventional Java IO channels are two way i.e can read as well as write.Java NIO channel supports asynchronous flow of data both in blocking and non blocking mode.

Implementations of Channel

Java NIO 管道主要在以下类中实现 −

Java NIO channel is implemented primarily in following classes −

  1. FileChannel − In order to read data from file we uses file channel. Object of file channel can be created only by calling the getChannel() method on file object as we can’t create file object directly.

  2. DatagramChannel − The datagram channel can read and write the data over the network via UDP (User Datagram Protocol).Object of DataGramchannel can be created using factory methods.

  3. SocketChannel − The SocketChannel channel can read and write the data over the network via TCP (Transmission Control Protocol). It also uses the factory methods for creating the new object.

  4. ServerSocketChannel − The ServerSocketChannel read and write the data over TCP connections, same as a web server. For every incoming connection a SocketChannel is created.

Example

以下示例从 C:/Test/temp.txt 的文本文件中读取数据,并将内容打印到控制台。

Following example reads from a text file from C:/Test/temp.txt and prints the content to the console.

temp.txt

Hello World!

ChannelDemo.java

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class ChannelDemo {
   public static void main(String args[]) throws IOException {
      RandomAccessFile file = new RandomAccessFile("C:/Test/temp.txt", "r");
      FileChannel fileChannel = file.getChannel();
      ByteBuffer byteBuffer = ByteBuffer.allocate(512);
      while (fileChannel.read(byteBuffer) > 0) {
         // flip the buffer to prepare for get operation
         byteBuffer.flip();
         while (byteBuffer.hasRemaining()) {
            System.out.print((char) byteBuffer.get());
         }
      }
      file.close();
   }
}

Output

Hello World!

Java NIO - File Channel

Description

如前所述,Java NIO 通道的 FileChannel 实现被引入以访问文件的元数据属性,包括创建、修改、大小等。除此之外,文件通道是多线程的,这再次使 Java NIO 比 Java IO 更有效。

As already mentioned FileChannel implementation of Java NIO channel is introduced to access meta data properties of the file including creation, modification, size etc.Along with this File Channels are multi threaded which again makes Java NIO more efficient than Java IO.

一般来说,我们可以说 FileChannel 是连接到文件的通道,通过它你可以从文件读取数据,并向文件写入数据。FileChannel 的另一个重要特征是它不能被设置为非阻塞模式,并且始终以阻塞模式运行。

In general we can say that FileChannel is a channel that is connected to a file by which you can read data from a file, and write data to a file.Other important characteristic of FileChannel is this that it cannot be set into non-blocking mode and always runs in blocking mode.

我们无法直接获取文件通道对象,文件通道对象可以通过以下方法获得:

We can’t get file channel object directly, Object of file channel is obtained either by −

  1. getChannel() − method on any either FileInputStream, FileOutputStream or RandomAccessFile.

  2. open() − method of File channel which by default open the channel.

文件通道的对象类型取决于调用对象创建时所调用的类类型,即如果对象是通过调用 FileInputStream 的 getchannel 方法创建的,则文件通道将被打开以进行读取,并且在尝试写入时将抛出 NonWritableChannelException。

The object type of File channel depends on type of class called from object creation i.e if object is created by calling getchannel method of FileInputStream then File channel is opened for reading and will throw NonWritableChannelException in case attempt to write to it.

Example

以下示例显示如何从 Java NIO FileChannel 读取和写入数据。

The following example shows the how to read and write data from Java NIO FileChannel.

以下示例从 C:/Test/temp.txt 的文本文件中读取数据,并将内容打印到控制台。

Following example reads from a text file from C:/Test/temp.txt and prints the content to the console.

temp.txt

Hello World!

FileChannelDemo.java

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.HashSet;
import java.util.Set;

public class FileChannelDemo {
   public static void main(String args[]) throws IOException {
      //append the content to existing file
      writeFileChannel(ByteBuffer.wrap("Welcome to TutorialsPoint".getBytes()));
      //read the file
      readFileChannel();
   }
   public static void readFileChannel() throws IOException {
      RandomAccessFile randomAccessFile = new RandomAccessFile("C:/Test/temp.txt",
      "rw");
      FileChannel fileChannel = randomAccessFile.getChannel();
      ByteBuffer byteBuffer = ByteBuffer.allocate(512);
      Charset charset = Charset.forName("US-ASCII");
      while (fileChannel.read(byteBuffer) > 0) {
         byteBuffer.rewind();
         System.out.print(charset.decode(byteBuffer));
         byteBuffer.flip();
      }
      fileChannel.close();
      randomAccessFile.close();
   }
   public static void writeFileChannel(ByteBuffer byteBuffer)throws IOException {
      Set<StandardOpenOption> options = new HashSet<>();
      options.add(StandardOpenOption.CREATE);
      options.add(StandardOpenOption.APPEND);
      Path path = Paths.get("C:/Test/temp.txt");
      FileChannel fileChannel = FileChannel.open(path, options);
      fileChannel.write(byteBuffer);
      fileChannel.close();
   }
}

Output

Hello World! Welcome to TutorialsPoint

Java NIO - Datagram Channel

Java NIO Datagram 用作在无连接协议上发送和接收 UDP 数据包的通道。默认情况下,数据报通道在阻塞,虽然它可以在非阻塞模式下使用。为了使它非阻塞,我们可以使用 configureBlocking(false) 方法。数据报通道可以通过调用其名为 open() 的静态方法之一来打开,该方法还可以使用 IP 地址作为参数,以便可以用于多播。

Java NIO Datagram is used as channel which can send and receive UDP packets over a connection less protocol.By default datagram channel is blocking while it can be use in non blocking mode.In order to make it non-blocking we can use the configureBlocking(false) method.DataGram channel can be open by calling its one of the static method named as open() which can also take IP address as parameter so that it can be used for multi casting.

数据报通道类似于 FileChannel,默认情况下不进行连接,因此为了实现连接,我们必须显式调用其 connect() 方法。然而,数据报通道不需要连接才能使用 send 和 receive 方法,而它必须连接才能使用 read 和 write 方法,因为那些方法不接受或不返回套接字地址。

Datagram channel alike of FileChannel do not connected by default in order to make it connected we have to explicitly call its connect() method.However datagram channel need not be connected in order for the send and receive methods to be used while it must be connected in order to use the read and write methods, since those methods do not accept or return socket addresses.

我们可以通过调用其 isConnected() 方法来检查数据报通道的连接状态。一旦连接,数据报通道将一直保持连接状态,直到它断开或关闭。数据报通道是线程安全的,并且同时支持多线程和并发。

We can check the connection status of datagram channel by calling its isConnected() method.Once connected, a datagram channel remains connected until it is disconnected or closed.Datagram channels are thread safe and supports multi-threading and concurrency simultaneously.

Important methods of datagram channel

  1. bind(SocketAddress local) − This method is used to bind the datagram channel’s socket to the local address which is provided as the parameter to this method.

  2. connect(SocketAddress remote) − This method is used to connect the socket to the remote address.

  3. disconnect() − This method is used to disconnect the socket to the remote address.

  4. getRemoteAddress() − This method return the address of remote location to which the channel’s socket is connected.

  5. isConnected() − As already mentioned this method returns the status of connection of datagram channel i.e whether it is connected or not.

  6. open() and open(ProtocolFamily family) − Open method is used open a datagram channel for single address while parametrized open method open channel for multiple addresses represented as protocol family.

  7. read(ByteBuffer dst) − This method is used to read data from the given buffer through datagram channel.

  8. receive(ByteBuffer dst) − This method is used to receive datagram via this channel.

  9. send(ByteBuffer src, SocketAddress target) − This method is used to send datagram via this channel.

Example

以下示例显示如何通过 Java NIO DataGramChannel 发送数据。

The following example shows the how to send data from Java NIO DataGramChannel.

Server: DatagramChannelServer.java

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;

public class DatagramChannelServer {
   public static void main(String[] args) throws IOException {
      DatagramChannel server = DatagramChannel.open();
      InetSocketAddress iAdd = new InetSocketAddress("localhost", 8989);
      server.bind(iAdd);
      System.out.println("Server Started: " + iAdd);
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      //receive buffer from client.
      SocketAddress remoteAdd = server.receive(buffer);
      //change mode of buffer
      buffer.flip();
      int limits = buffer.limit();
      byte bytes[] = new byte[limits];
      buffer.get(bytes, 0, limits);
      String msg = new String(bytes);
      System.out.println("Client at " + remoteAdd + "  sent: " + msg);
      server.send(buffer,remoteAdd);
      server.close();
   }
}

Output

Server Started: localhost/127.0.0.1:8989

Client: DatagramChannelClient.java

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;

public class DatagramChannelClient {
   public static void main(String[] args) throws IOException {
      DatagramChannel client = null;
      client = DatagramChannel.open();

      client.bind(null);

      String msg = "Hello World!";
      ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
      InetSocketAddress serverAddress = new InetSocketAddress("localhost",
        8989);

      client.send(buffer, serverAddress);
      buffer.clear();
      client.receive(buffer);
      buffer.flip();

      client.close();
   }
}

Output

运行客户端将在服务器上打印以下输出。

Running the client will print the following output on server.

Server Started: localhost/127.0.0.1:8989
Client at /127.0.0.1:64857  sent: Hello World!

Java NIO - Socket Channel

Java NIO 套接字通道是一种可选择类型通道,这意味着它可以使用选择器多路复用,用于面向流的数据流连接套接字。套接字通道可以通过调用其静态 open() 方法创建,前提是没有任何预先存在的套接字。套接字通道是通过调用 open 方法创建的,但尚未连接。为了连接套接字通道,需要调用 connect() 方法。这里需要注意的一点是,如果通道未连接并且试图尝试任何 I/O 操作,则此通道将抛出 NotYetConnectedException。因此,必须确保在执行任何 IO 操作之前通道已连接。一旦通道连接上,它将保持连接状态,直到它被关闭。可以通过调用其 isConnected 方法来确定套接字通道的状态。

Java NIO socket channel is a selectable type channel which means it can be multiplexed using selector, used for stream oriented data flow connecting sockets.Socket channel can be created by invoking its static open() method,providing any pre-existing socket is not already present.Socket channel is created by invoking open method but not yet connected.In order to connect socket channel connect() method is to be called.One point to be mentioned here is if channel is not connected and any I/O operation is tried to be attempted then NotYetConnectedException is thrown by this channel.So one must be ensure that channel is connected before performing any IO operation.Once channel is get connected,it remains connected until it is closed.The state of socket channel may be determined by invoking its isConnected method.

套接字通道的连接可以通过调用其 finishConnect() 方法完成。是否正在进行连接操作可以通过调用 isConnectionPending 方法来确定。默认情况下,套接字通道支持非阻塞连接。它还支持异步关闭,这类似于通道类中指定的异步关闭操作。

The connection of socket channel could be finished by invoking its finishConnect() method.Whether or not a connection operation is in progress may be determined by invoking the isConnectionPending method.By default socket channel supports non-blocking connection.Also it support asynchronous shutdown, which is similar to the asynchronous close operation specified in the Channel class.

套接字通道可以安全地供多个并发线程使用。它们支持并发读和写,尽管最多只有一个线程可以读取,并且最多只有一个线程可以在任何给定时间写入。connect 和 finishConnect 方法彼此互斥同步,并且在其中一个方法的调用正在进行时尝试启动读取或写入操作将阻塞,直到该调用完成。

Socket channels are safe for use by multiple concurrent threads. They support concurrent reading and writing, though at most one thread may be reading and at most one thread may be writing at any given time. The connect and finishConnect methods are mutually synchronized against each other, and an attempt to initiate a read or write operation while an invocation of one of these methods is in progress will block until that invocation is complete.

Important methods of Socket channel

  1. bind(SocketAddress local) − This method is used to bind the socket channel to the local address which is provided as the parameter to this method.

  2. connect(SocketAddress remote) − This method is used to connect the socket to the remote address.

  3. finishConnect() − This method is used to finishes the process of connecting a socket channel.

  4. getRemoteAddress() − This method return the address of remote location to which the channel’s socket is connected.

  5. isConnected() − As already mentioned this method returns the status of connection of socket channel i.e whether it is connected or not.

  6. open() and open((SocketAddress remote) − Open method is used open a socket channel for no specified address while parameterized open method open channel for specified remote address and also connects to it.This convenience method works as if by invoking the open() method, invoking the connect method upon the resulting socket channel, passing it remote, and then returning that channel.

  7. read(ByteBuffer dst) − This method is used to read data from the given buffer through socket channel.

  8. isConnectionPending() − This method tells whether or not a connection operation is in progress on this channel.

Example

以下示例说明如何从 Java NIO SocketChannel 发送数据。

The following example shows the how to send data from Java NIO SocketChannel.

C:/Test/temp.txt

Hello World!

Client: SocketChannelClient.java

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.EnumSet;

public class SocketChannelClient {
   public static void main(String[] args) throws IOException {
      ServerSocketChannel serverSocket = null;
      SocketChannel client = null;
      serverSocket = ServerSocketChannel.open();
      serverSocket.socket().bind(new InetSocketAddress(9000));
      client = serverSocket.accept();
      System.out.println("Connection Set:  " + client.getRemoteAddress());
      Path path = Paths.get("C:/Test/temp1.txt");
      FileChannel fileChannel = FileChannel.open(path,
         EnumSet.of(StandardOpenOption.CREATE,
            StandardOpenOption.TRUNCATE_EXISTING,
            StandardOpenOption.WRITE)
         );
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      while(client.read(buffer) > 0) {
         buffer.flip();
         fileChannel.write(buffer);
         buffer.clear();
      }
      fileChannel.close();
      System.out.println("File Received");
      client.close();
   }
}

Output

在服务器启动前运行客户端不会打印任何内容。

Running the client will not print anything until server starts.

Server: SocketChannelServer.java

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Path;
import java.nio.file.Paths;

public class SocketChannelServer {
   public static void main(String[] args) throws IOException {
      SocketChannel server = SocketChannel.open();
      SocketAddress socketAddr = new InetSocketAddress("localhost", 9000);
      server.connect(socketAddr);

      Path path = Paths.get("C:/Test/temp.txt");
      FileChannel fileChannel = FileChannel.open(path);
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      while(fileChannel.read(buffer) > 0) {
         buffer.flip();
         server.write(buffer);
         buffer.clear();
      }
      fileChannel.close();
      System.out.println("File Sent");
      server.close();
   }
}

Output

运行服务器将打印以下内容。

Running the server will print the following.

Connection Set:  /127.0.0.1:49558
File Received

Java NIO - ServerSocket Channel

Java NIO 服务器套接字通道是再次用于流式数据流连接套接字的可选择类型通道。服务器套接字通道可通过唤起其静态` open() 方法(在没有预先存在套接字的情况下提供)进行创建。通过唤起开放方法创建服务器套接字通道,但还未绑定。为了绑定套接字通道,需要调用 bind() `方法。

Java NIO server socket channel is again a selectable type channel used for stream oriented data flow connecting sockets.Server Socket channel can be created by invoking its static open() method,providing any pre-existing socket is not already present.Server Socket channel is created by invoking open method but not yet bound.In order to bound socket channel bind() method is to be called.

这里需要提到的一点是,如果通道没有绑定,并且尝试进行任何 I/O 操作,则此通道会抛出 NotYetBoundException。因此,在执行任何 IO 操作之前,必须确保通道已绑定。

One point to be mentioned here is if channel is not bound and any I/O operation is tried to be attempted then NotYetBoundException is thrown by this channel.So one must be ensure that channel is bounded before performing any IO operation.

通过调用 ServerSocketChannel.accept() 方法监听服务器套接字通道的传入连接。当 accept() 方法返回时,它会返回具有传入连接的 SocketChannel。因此,accept() 方法会一直阻止,直到传入连接到达为止。如果通道处于非阻塞模式,那么在没有挂起连接的情况下,accept 方法会立即返回 null。否则,它会无限期阻止,直到有可用的新连接或发生 I/O 错误。

Incoming connections for the server socket channel are listen by calling the ServerSocketChannel.accept() method. When the accept() method returns, it returns a SocketChannel with an incoming connection. Thus, the accept() method blocks until an incoming connection arrives.If the channel is in non-blocking mode then accept method will immediately return null if there are no pending connections. Otherwise it will block indefinitely until a new connection is available or an I/O error occurs.

新通道的套接字最初未绑定;必须通过其套接字的绑定方法之一将其绑定到特定地址,才能接受连接。新通道也可以通过唤起系统范围的默认 SelectorProvider 对象的 openServerSocketChannel 方法进行创建。

The new channel’s socket is initially unbound; it must be bound to a specific address via one of its socket’s bind methods before connections can be accepted.Also the new channel is created by invoking the openServerSocketChannel method of the system-wide default SelectorProvider object.

与套接字通道类似,服务器套接字通道可以使用` read() `方法读取数据。首先分配缓冲区。从 ServerSocketChannel 读入的数据存储在缓冲区中。其次,我们调用 ServerSocketChannel.read() 方法,它将数据从 ServerSocketChannel 读入缓冲区。read() 方法的整数值返回写入缓冲区的字节数。

Like socket channel server socket channel could read data using read() method.Firstly the buffer is allocated. The data read from a ServerSocketChannel is stored into the buffer.Secondly we call the ServerSocketChannel.read() method and it reads the data from a ServerSocketChannel into a buffer. The integer value of the read() method returns how many bytes were written into the buffer

同样地,可以使用` write() `方法将数据写入服务器套接字通道,并使用缓冲区作为参数。通常在 while 循环中使用 write 方法,因为需要重复 write() 方法,直到缓冲区中没有可写入的可用字节为止。

Similarly data could be written to server socket channel using write() method using buffer as a parameter.Commonly uses write method in a while loop as need to repeat the write() method until the Buffer has no further bytes available to write.

Important methods of Socket channel

  1. bind(SocketAddress local) − This method is used to bind the socket channel to the local address which is provided as the parameter to this method.

  2. accept() − This method is used to accepts a connection made to this channel’s socket.

  3. connect(SocketAddress remote) − This method is used to connect the socket to the remote address.

  4. finishConnect() − This method is used to finishes the process of connecting a socket channel.

  5. getRemoteAddress() − This method return the address of remote location to which the channel’s socket is connected.

  6. * isConnected()* − As already mentioned this method returns the status of connection of socket channel i.e whether it is connected or not.

  7. open() − Open method is used open a socket channel for no specified address.This convenience method works as if by invoking the open() method, invoking the connect method upon the resulting server socket channel, passing it remote, and then returning that channel.

  8. read(ByteBuffer dst) − This method is used to read data from the given buffer through socket channel.

  9. setOption(SocketOption<T> name, T value) − This method sets the value of a socket option.

  10. socket() − This method retrieves a server socket associated with this channel.

  11. validOps() − This method returns an operation set identifying this channel’s supported operations.Server-socket channels only support the accepting of new connections, so this method returns SelectionKey.OP_ACCEPT.

Example

以下示例显示如何从 Java NIO ServerSocketChannel 发送数据。

The following example shows the how to send data from Java NIO ServerSocketChannel.

C:/Test/temp.txt

Hello World!

Client: SocketChannelClient.java

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.EnumSet;

public class SocketChannelClient {
   public static void main(String[] args) throws IOException {
      ServerSocketChannel serverSocket = null;
      SocketChannel client = null;
      serverSocket = ServerSocketChannel.open();
      serverSocket.socket().bind(new InetSocketAddress(9000));
      client = serverSocket.accept();
      System.out.println("Connection Set:  " + client.getRemoteAddress());
      Path path = Paths.get("C:/Test/temp1.txt");
      FileChannel fileChannel = FileChannel.open(path,
         EnumSet.of(StandardOpenOption.CREATE,
            StandardOpenOption.TRUNCATE_EXISTING,
            StandardOpenOption.WRITE)
         );
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      while(client.read(buffer) > 0) {
         buffer.flip();
         fileChannel.write(buffer);
         buffer.clear();
      }
      fileChannel.close();
      System.out.println("File Received");
      client.close();
   }
}

Output

在服务器启动前运行客户端不会打印任何内容。

Running the client will not print anything until server starts.

Server: SocketChannelServer.java

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Path;
import java.nio.file.Paths;

public class SocketChannelServer {
   public static void main(String[] args) throws IOException {
      SocketChannel server = SocketChannel.open();
      SocketAddress socketAddr = new InetSocketAddress("localhost", 9000);
      server.connect(socketAddr);
      Path path = Paths.get("C:/Test/temp.txt");
      FileChannel fileChannel = FileChannel.open(path);
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      while(fileChannel.read(buffer) > 0) {
         buffer.flip();
         server.write(buffer);
         buffer.clear();
      }
      fileChannel.close();
      System.out.println("File Sent");
      server.close();
   }
}

Output

运行服务器将打印以下内容。

Running the server will print the following.

Connection Set:  /127.0.0.1:49558
File Received

Java NIO - Scatter

众所周知,与 Java 的传统 IO API 相比,Java NIO 是一个针对数据 IO 操作进行了更优化的 API。Java NIO 提供的另外一项支持是将数据从多个缓冲区读/写到通道。这种多读多写支持称为分散和聚集,其中多个缓冲区从读取数据时单个通道中分散,而多个缓冲区在写入数据时从单个通道中聚集。

As we know that Java NIO is a more optimized API for data IO operations as compared to the conventional IO API of Java.One more additional support which Java NIO provides is to read/write data from/to multiple buffers to channel.This multiple read and write support is termed as Scatter and Gather in which data is scattered to multiple buffers from single channel in case of read data while data is gathered from multiple buffers to single channel in case of write data.

为了实现从通道的多读多写,Java NIO 为读取和写入数据提供了 ScatteringByteChannel 和 GatheringByteChannel API,如以下示例所示。

In order to achieve this multiple read and write from channel there is ScatteringByteChannel and GatheringByteChannel API which Java NIO provides for read and write the data as illustrate in below example.

ScatteringByteChannel

Read from multiple channels - 在此处,我们从单个通道中读取数据到多个缓冲区。为此,分配多个缓冲区并将其添加到缓冲区类型数组中。然后,将此数组作为参数传递给 ScatteringByteChannel read() 方法,该方法随后以数组中缓冲区出现的顺序从通道写入数据。一个缓冲区填满后,通道将填充下一个缓冲区。

Read from multiple channels − In this we made to reads data from a single channel into multiple buffers.For this multiple buffers are allocated and are added to a buffer type array.Then this array is passed as parameter to the ScatteringByteChannel read() method which then writes data from the channel in the sequence the buffers occur in the array.Once a buffer is full, the channel moves on to fill the next buffer.

以下示例说明如何在 Java NIO 中执行数据分散。

The following example shows how scattering of data is performed in Java NIO

C:/Test/temp.txt

Hello World!
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ScatteringByteChannel;

public class ScatterExample {
   private static String FILENAME = "C:/Test/temp.txt";
   public static void main(String[] args) {
      ByteBuffer bLen1 = ByteBuffer.allocate(1024);
      ByteBuffer bLen2 = ByteBuffer.allocate(1024);
      FileInputStream in;
      try {
         in = new FileInputStream(FILENAME);
         ScatteringByteChannel scatter = in.getChannel();
         scatter.read(new ByteBuffer[] {bLen1, bLen2});
         bLen1.position(0);
         bLen2.position(0);
         int len1 = bLen1.asIntBuffer().get();
         int len2 = bLen2.asIntBuffer().get();
         System.out.println("Scattering : Len1 = " + len1);
         System.out.println("Scattering : Len2 = " + len2);
      }
      catch (FileNotFoundException exObj) {
         exObj.printStackTrace();
      }
      catch (IOException ioObj) {
         ioObj.printStackTrace();
      }
   }
}

Output

Scattering : Len1 = 1214606444
Scattering : Len2 = 0

最后可以得出结论,如果正确使用,Java NIO 中的分散/聚集方法是一种经过优化且可以多任务处理。它允许你将分离出读入多个存储桶中的数据或将不同数据块组装成一个整体的繁琐工作委托给操作系统。毫无疑问,这节省了时间,并且通过避免缓冲区副本更有效地使用了操作系统,并减少了需要编写和调试的代码量。

In last it can be concluded that scatter/gather approach in Java NIO is introduced as an optimized and multitasked when used properly.It allows you to delegate to the operating system the grunt work of separating out the data you read into multiple buckets, or assembling disparate chunks of data into a whole.No doubt this saves time and uses operating system more efficiently by avoiding buffer copies, and reduces the amount of code need to write and debug.

Java NIO - Gather

众所周知,与 Java 的传统 IO API 相比,Java NIO 是一个针对数据 IO 操作进行了更优化的 API。Java NIO 提供的另外一项支持是将数据从多个缓冲区读/写到通道。这种多读多写支持称为分散和聚集,其中多个缓冲区从读取数据时单个通道中分散,而多个缓冲区在写入数据时从单个通道中聚集。

As we know that Java NIO is a more optimized API for data IO operations as compared to the conventional IO API of Java.One more additional support which Java NIO provides is to read/write data from/to multiple buffers to channel.This multiple read and write support is termed as Scatter and Gather in which data is scattered to multiple buffers from single channel in case of read data while data is gathered from multiple buffers to single channel in case of write data.

为了实现从通道的多读多写,Java NIO 为读取和写入数据提供了 ScatteringByteChannel 和 GatheringByteChannel API,如以下示例所示。

In order to achieve this multiple read and write from channel there is ScatteringByteChannel and GatheringByteChannel API which Java NIO provides for read and write the data as illustrate in below example.

GatheringByteChannel

write to multiple channels − 在此示例中,我们让多个缓冲区中的数据写入一个管道中。为此,再次分配了多个缓冲区,并将这些缓冲区添加到缓冲区类型数组中。然后将此数组作为参数传递给 GatheringByteChannel write() 方法,该方法依次从数组中的多个缓冲区写入数据。这里需要记住的一点是,只写入缓冲区的 position 和 limit 之间的数据。

write to multiple channels − In this we made to write data from multiple buffers into a single channel.For this again multiple buffers are allocated and are added to a buffer type array.Then this array is passed as parameter to the GatheringByteChannel write() method which then writes data from the multiple buffers in the sequence the buffers occur in the array.One point to remember here is only the data between the position and the limit of the buffers are written.

以下示例显示了如何在 Java NIO 中执行数据收集

The following example shows how data gathering is performed in Java NIO

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.GatheringByteChannel;

public class GatherExample {
   private static String FILENAME = "C:/Test/temp.txt";
   public static void main(String[] args) {
      String stream1 = "Gather data stream first";
      String stream2 = "Gather data stream second";
      ByteBuffer bLen1 = ByteBuffer.allocate(1024);
      ByteBuffer bLen2 = ByteBuffer.allocate(1024);
      // Next two buffer hold the data we want to write
      ByteBuffer bstream1 = ByteBuffer.wrap(stream1.getBytes());
      ByteBuffer bstream2 = ByteBuffer.wrap(stream2.getBytes());
      int len1 = stream1.length();
      int len2 = stream2.length();
      // Writing length(data) to the Buffer
      bLen1.asIntBuffer().put(len1);
      bLen2.asIntBuffer().put(len2);
      System.out.println("Gathering : Len1 = " + len1);
      System.out.println("Gathering : Len2 = " + len2);
      // Write data to the file
      try {
         FileOutputStream out = new FileOutputStream(FILENAME);
         GatheringByteChannel gather = out.getChannel();
         gather.write(new ByteBuffer[] {bLen1, bLen2, bstream1, bstream2});
         out.close();
         gather.close();
      }
      catch (FileNotFoundException exObj) {
         exObj.printStackTrace();
      }
      catch(IOException ioObj) {
         ioObj.printStackTrace();
      }
   }
}

Output

Gathering : Len1 = 24
Gathering : Len2 = 25

最后可以得出结论,如果正确使用,Java NIO 中的分散/聚集方法是一种经过优化且可以多任务处理。它允许你将分离出读入多个存储桶中的数据或将不同数据块组装成一个整体的繁琐工作委托给操作系统。毫无疑问,这节省了时间,并且通过避免缓冲区副本更有效地使用了操作系统,并减少了需要编写和调试的代码量。

In last it can be concluded that scatter/gather approach in Java NIO is introduced as an optimized and multitasked when used properly.It allows you to delegate to the operating system the grunt work of separating out the data you read into multiple buckets, or assembling disparate chunks of data into a whole.No doubt this saves time and uses operating system more efficiently by avoiding buffer copies, and reduces the amount of code need to write and debug.

Java NIO - Buffer

Java NIO 中的缓冲区可以看作一个简单的对象,它充当定大小数据块的容器,可用于向通道写入数据或从通道读取数据,以便缓冲区充当通道的端点。

Buffers in Java NIO can be treated as a simple object which act as a fixed sized container of data chunks that can be used to write data to channel or read data from channel so that buffers act as endpoints to the channels.

它提供了成套的方法,以便于处理内存块以将数据读入通道或从通道写入数据。

It provide set of methods that make more convenient to deal with memory block in order to read and write data to and from channels.

与经典 IO 相比,缓冲区使得 NIO 包更有效率且更快速,因为在 IO 的情况下,数据是以流的形式处理的,不支持异步和并发的数据流。此外,IO 也不允许以块或字节组的形式执行数据。

Buffers makes NIO package more efficient and faster as compared to classic IO as in case of IO data is deal in the form of streams which do not support asynchronous and concurrent flow of data.Also IO does not allow data execution in chunk or group of bytes.

定义 Java NIO 缓冲区的主要参数可以定义为:

Primary parameters that defines Java NIO buffer could be defined as −

  1. Capacity − Maximum Amount of data/byte that can be stored in the Buffer.Capacity of a buffer can not be altered.Once the buffer is full it should be cleared before writing to it.

  2. Limit − Limit has meaning as per the mode of Buffer i.e. in write mode of Buffer Limit is equal to the capacity which means that maximum data that could be write in buffer.While in read mode of buffer Limit means the limit of how much data can be read from the Buffer.

  3. Position − Points to the current location of cursor in buffer.Initially setted as 0 at the time of creation of buffer or in other words it is the index of the next element to be read or written which get updated automatically by get() and put() methods.

  4. Mark − Mark a bookmark of the position in a buffer.When mark() method is called the current position is recorded and when reset() is called the marked position is restored.

Buffer Type

Java NIO 缓冲区可以根据缓冲区处理的数据类型进行以下分类:

Java NIO buffers can be classified in following variants on the basis of data types the buffer deals with −

  1. ByteBuffer

  2. MappedByteBuffer

  3. CharBuffer

  4. DoubleBuffer

  5. FloatBuffer

  6. IntBuffer

  7. LongBuffer

  8. ShortBuffer

Important methods of Buffer

如前所述,Buffer 充当内存对象,它提供了一组方法,使处理内存块更加方便。以下是 Buffer 的重要方法:

As mentioned already that Buffer act as memory object which provide set of methods that make more convenient to deal with memory block.Following are the important methods of Buffer −

  1. allocate(int capacity) − This method is use to allocate a new buffer with capacity as parameter.Allocate method throws IllegalArgumentException in case the passed capacity is a negative integer.

  2. read() and put() − read method of channel is used to write data from channel to buffer while put is a method of buffer which is used to write data in buffer.

  3. flip() − The flip method switches the mode of Buffer from writing to reading mode.It also sets the position back to 0, and sets the limit to where position was at time of writing.

  4. write() and get() − write method of channel is used to write data from buffer to channel while get is a method of buffer which is used to read data from buffer.

  5. rewind() − rewind method is used when reread is required as it sets the position back to zero and do not alter the value of limit.

  6. clear() and compact() − clear and compact both methods are used to make buffer from read to write mode.clear() method makes the position to zero and limit equals to capacity,in this method the data in the buffer is not cleared only the markers get re initialized. On other hand compact() method is use when there remained some un-read data and still we use write mode of buffer in this case compact method copies all unread data to the beginning of the buffer and sets position to right after the last unread element.The limit property is still set to capacity.

  7. mark() and reset() − As name suggest mark method is used to mark any particular position in a buffer while reset make position back to marked position.

Example

以下示例显示了上面定义的方法的实现。

The following example shows the implementation of above defined methods.

import java.nio.ByteBuffer;
import java.nio.CharBuffer;

public class BufferDemo {
   public static void main (String [] args) {
      //allocate a character type buffer.
      CharBuffer buffer = CharBuffer.allocate(10);
      String text = "bufferDemo";
      System.out.println("Input text: " + text);
      for (int i = 0; i < text.length(); i++) {
         char c = text.charAt(i);
         //put character in buffer.
		 buffer.put(c);
      }
      int buffPos = buffer.position();
      System.out.println("Position after data is written into buffer: " + buffPos);
      buffer.flip();
      System.out.println("Reading buffer contents:");
      while (buffer.hasRemaining()) {
         System.out.println(buffer.get());
      }
      //set the position of buffer to 5.
      buffer.position(5);
      //sets this buffer's mark at its position
      buffer.mark();
      //try to change the position
      buffer.position(6);
      //calling reset method to restore to the position we marked.
      //reset() raise InvalidMarkException if either the new position is less
      //than the position marked or merk has not been setted.
      buffer.reset();
      System.out.println("Restored buffer position : " + buffer.position());
   }
}

Output

Input text: bufferDemo
Position after data is written into buffer: 10
Reading buffer contents:
b
u
f
f
e
r
D
e
m
o
Restored buffer position : 5

Java NIO - Selector

如我们所知,Java NIO 支持从多个通道和缓冲区进行多个事务。因此,为了检查一个或多个 NIO 通道并确定哪些通道已准备好进行数据事务,即读取或写入,Java NIO 提供选择器。

As we know that Java NIO supports multiple transaction from and to channels and buffer.So in order to examine one or more NIO Channel’s, and determine which channels are ready for data transaction i.e reading or writing Java NIO provide Selector.

使用选择器,我们可以让线程知道哪个通道已准备好进行数据写入和读取,并处理特定通道。

With Selector we can make a thread to know that which channel is ready for data writing and reading and could deal that particular channel.

我们可以通过调用其静态方法 open() 来获取选择器实例。在打开选择器后,我们必须向其注册非阻塞模式通道,这将返回 SelectionKey 实例。

We can get selector instance by calling its static method open().After open selector we have to register a non blocking mode channel with it which returns a instance of SelectionKey.

SelectionKey 基本上是可以使用通道执行的操作集合,或者可以说我们可以借助选择器密钥了解通道的状态。

SelectionKey is basically a collection of operations that can be performed with channel or we can say that we could know the state of channel with the help of selection key.

表示选择器密钥通道的主要操作或状态是:

The major operations or state of channel represented by selection key are −

  1. SelectionKey.OP_CONNECT − Channel which is ready to connect to server.

  2. SelectionKey.OP_ACCEPT − Channel which is ready to accept incoming connections.

  3. SelectionKey.OP_READ − Channel which is ready to data read.

  4. SelectionKey.OP_WRITE − Channel which is ready to data write.

注册后获得的选择器密钥具有以下几个重要方法:

Selection key obtained after registration has some important methods as mentioned below −

  1. attach() − This method is used to attach an object with the key.The main purpose of attaching an object to a channel is to recognizing the same channel.

  2. attachment() − This method is used to retain the attached object from the channel.

  3. channel() − This method is used to get the channel for which the particular key is created.

  4. selector() − This method is used to get the selector for which the particular key is created.

  5. isValid() − This method returns weather the key is valid or not.

  6. isReadable() − This method states that weather key’s channel is ready for read or not.

  7. isWritable() − This method states that weather key’s channel is ready for write or not.

  8. isAcceptable() − This method states that weather key’s channel is ready for accepting incoming connection or not.

  9. isConnectable() − This method tests whether this key’s channel has either finished, or failed to finish, its socket-connection operation.

  10. isAcceptable() − This method tests whether this key’s channel is ready to accept a new socket connection.

  11. interestOps() − This method retrieves this key’s interest set.

  12. readyOps() − This method retrieves the ready set which is the set of operations the channel is ready for.

通过调用其静态方法 select() ,我们可以从选择器中选择通道。选择器的选择方法被重载为 -

We can select a channel from selector by calling its static method select().Select method of selector is overloaded as −

  1. select() − This method blocks the current thread until at least one channel is ready for the events it is registered for.

  2. select(long timeout) − This method does the same as select() except it blocks the thread for a maximum of timeout milliseconds (the parameter).

  3. selectNow() − This method doesn’t block at all.It returns immediately with whatever channels are ready.

此外,为了离开调用选择方法的阻塞线程,可以在选择器实例中调用 wakeup() 方法,然后在 select() 中等待的线程将立即返回。

Also in order to leave a blocked thread which call out select method,wakeup() method can be called from selector instance after which the thread waiting inside select() will then return immediately.

最后,我们可以通过调用 close() 方法关闭选择器,该方法还会使与此选择器注册的所有 SelectionKey 实例无效,同时关闭选择器。

In last we can close the selector by calling close() method which also invalidates all SelectionKey instances registered with this Selector along with closing the selector.

Example

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class SelectorDemo {
   public static void main(String[] args) throws IOException {
      String demo_text = "This is a demo String";
      Selector selector = Selector.open();
      ServerSocketChannel serverSocket = ServerSocketChannel.open();
      serverSocket.bind(new InetSocketAddress("localhost", 5454));
      serverSocket.configureBlocking(false);
      serverSocket.register(selector, SelectionKey.OP_ACCEPT);
      ByteBuffer buffer = ByteBuffer.allocate(256);
      while (true) {
         selector.select();
         Set<SelectionKey> selectedKeys = selector.selectedKeys();
         Iterator<SelectionKey> iter = selectedKeys.iterator();
         while (iter.hasNext()) {
            SelectionKey key = iter.next();
            int interestOps = key.interestOps();
            System.out.println(interestOps);
            if (key.isAcceptable()) {
               SocketChannel client = serverSocket.accept();
               client.configureBlocking(false);
               client.register(selector, SelectionKey.OP_READ);
            }
            if (key.isReadable()) {
               SocketChannel client = (SocketChannel) key.channel();
               client.read(buffer);
               if (new String(buffer.array()).trim().equals(demo_text)) {
                  client.close();
                  System.out.println("Not accepting client messages anymore");
               }
               buffer.flip();
               client.write(buffer);
               buffer.clear();
            }
            iter.remove();
         }
      }
   }
}

Java NIO - Pipe

在 Java NIO 中,管道是一个用于在两个线程之间写入和读取数据的组件。管道主要由负责数据传播的两个通道组成。

In Java NIO pipe is a component which is used to write and read data between two threads.Pipe mainly consist of two channels which are responsible for data propagation.

在这两个组成通道中,一个被称为接收通道,主要用于写入数据,另一个是源通道,其主要目的是从接收通道读取数据。

Among two constituent channels one is called as Sink channel which is mainly for writing data and other is Source channel whose main purpose is to read data from Sink channel.

在数据写入和读取期间按顺序保持数据同步,必须确保以写入管道顺序读取数据。

Data synchronization is kept in order during data writing and reading as it must be ensured that data must be read in a same order in which it is written to the Pipe.

必须注意,管道中的数据是单向流动的,即仅写入接收通道,只能从源通道读取数据。

It must kept in notice that it is a unidirectional flow of data in Pipe i.e data is written in Sink channel only and could only be read from Source channel.

在 Java NIO 中,管道被定义为一个抽象类,主要有三个方法,其中两个是抽象的。

In Java NIO pipe is defined as a abstract class with mainly three methods out of which two are abstract.

Methods of Pipe class

  1. open() − This method is used get an instance of Pipe or we can say pipe is created by calling out this method.

  2. sink() − This method returns the Pipe’s sink channel which is used to write data by calling its write method.

  3. source() − This method returns the Pipe’s source channel which is used to read data by calling its read method.

Example

以下示例显示 Java NIO 管道实现。

The following example shows the implementation of Java NIO pipe.

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Pipe;

public class PipeDemo {
   public static void main(String[] args) throws IOException {
      //An instance of Pipe is created
      Pipe pipe = Pipe.open();
      // gets the pipe's sink channel
      Pipe.SinkChannel skChannel = pipe.sink();
      String testData = "Test Data to Check java NIO Channels Pipe.";
      ByteBuffer buffer = ByteBuffer.allocate(512);
      buffer.clear();
      buffer.put(testData.getBytes());
      buffer.flip();
      //write data into sink channel.
      while(buffer.hasRemaining()) {
         skChannel.write(buffer);
      }
      //gets  pipe's source channel
      Pipe.SourceChannel sourceChannel = pipe.source();
      buffer = ByteBuffer.allocate(512);
      //write data into console
      while(sourceChannel.read(buffer) > 0){
         //limit is set to current position and position is set to zero
         buffer.flip();
         while(buffer.hasRemaining()){
            char ch = (char) buffer.get();
            System.out.print(ch);
         }
         //position is set to zero and limit is set to capacity to clear the buffer.
         buffer.clear();
      }
   }
}

Output

Test Data to Check java NIO Channels Pipe.

假设我们有一个文本文件 c:/test.txt ,其中包含以下内容。此文件将用作示例程序的输入。

Assuming we have a text file c:/test.txt, which has the following content. This file will be used as an input for our example program.

Java NIO - Path

顾名思义,路经是文件系统中诸如文件或目录之类的实体所在特定位置,以便人们可以在该特定位置搜索和访问它。

As name suggests Path is the particular location of an entity such as file or a directory in a file system so that one can search and access it at that particular location.

从技术上讲,在 Java 的术语中,路经是一个在 Java 7 版本中引入到 Java NIO 文件包中的接口,并且是特定文件系统中位置的表示。由于路经接口在 Java NIO 包中,因此它的限定名称为 java.nio.file.Path。

Technically in terms of Java, Path is an interface which is introduced in Java NIO file package during Java version 7,and is the representation of location in particular file system.As path interface is in Java NIO package so it get its qualified name as java.nio.file.Path.

一般来说,实体的路经可以是两種類型,一种是绝对路徑,另一種是相对路徑。正如两种路徑的名称所示,绝对路徑是从根目錄到該实体所在位置的位置地址,而相對路徑是相对于其他一些路徑的位置地址。Path在其定义中使用分隔符,在 Windows 中使用“\”,而在 UNIX 操作系统中使用“/”。

In general path of an entity could be of two types one is absolute path and other is relative path.As name of both paths suggests that absolute path is the location address from the root to the entity where it locates while relative path is the location address which is relative to some other path.Path uses delimiters in its definition as "\" for Windows and "/" for unix operating systems.

为了获取 Path 的实例,可以使用 java.nio.file.Paths 类` get() `的静态方法。此方法将路经字符串或一串连接后形成路经字符串的字符串转换为 Path 实例。如果传递的参数包含非法字符,此方法还会抛出运行时 InvalidPathException。

In order to get the instance of Path we can use static method of java.nio.file.Paths class get().This method converts a path string, or a sequence of strings that when joined form a path string, to a Path instance.This method also throws runtime InvalidPathException if the arguments passed contains illegal characters.

如上所述,可以通过传递根元素和查找文件所需的完整目录列表来检索绝对路径。而可以通过将基路径与相对路径结合来检索相对路径。将在以下示例中说明如何检索这两个路径

As mentioned above absolute path is retrieved by passing root element and the complete directory list required to locate the file.While relative path could be retrieved by combining the base path with the relative path.Retrieval of both paths would be illustrated in following example

Example

package com.java.nio;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.file.FileSystem;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathDemo {
   public static void main(String[] args) throws IOException {
      Path relative = Paths.get("file2.txt");
      System.out.println("Relative path: " + relative);
      Path absolute = relative.toAbsolutePath();
      System.out.println("Absolute path: " + absolute);
   }
}

到目前为止,我们知道什么是路径接口,为什么我们需要它以及如何访问它。现在,我们会了解到路径接口为我们提供哪些重要的方法。

So far we know that what is path interface why do we need that and how could we access it.Now we would know what are the important methods which Path interface provide us.

Important methods of Path Interface

  1. getFileName() − Returns the file system that created this object.

  2. getName() − Returns a name element of this path as a Path object.

  3. getNameCount() − Returns the number of name elements in the path.

  4. subpath() − Returns a relative Path that is a subsequence of the name elements of this path.

  5. getParent() − Returns the parent path, or null if this path does not have a parent.

  6. getRoot() − Returns the root component of this path as a Path object, or null if this path does not have a root component.

  7. toAbsolutePath() − Returns a Path object representing the absolute path of this path.

  8. toRealPath() − Returns the real path of an existing file.

  9. toFile() − Returns a File object representing this path.

  10. normalize() − Returns a path that is this path with redundant name elements eliminated.

  11. compareTo(Path other) − Compares two abstract paths lexicographically.This method returns zero if the argument is equal to this path, a value less than zero if this path is lexicographically less than the argument, or a value greater than zero if this path is lexicographically greater than the argument.

  12. endsWith(Path other) − Tests if this path ends with the given path.If the given path has N elements, and no root component, and this path has N or more elements, then this path ends with the given path if the last N elements of each path, starting at the element farthest from the root, are equal.

  13. endsWith(String other) − Tests if this path ends with a Path, constructed by converting the given path string, in exactly the manner specified by the endsWith(Path) method.

Example

以下示例说明了上面提到的 Path 接口的不同方法 −

Following example illustartes the different methods of Path interface which are mentioned above −

package com.java.nio;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.file.FileSystem;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathDemo {
   public static void main(String[] args) throws IOException {
      Path path = Paths.get("D:/workspace/ContentW/Saurav_CV.docx");
      FileSystem fs =  path.getFileSystem();
      System.out.println(fs.toString());
      System.out.println(path.isAbsolute());
      System.out.println(path.getFileName());
      System.out.println(path.toAbsolutePath().toString());
      System.out.println(path.getRoot());
      System.out.println(path.getParent());
      System.out.println(path.getNameCount());
      System.out.println(path.getName(0));
      System.out.println(path.subpath(0, 2));
      System.out.println(path.toString());
      System.out.println(path.getNameCount());
      Path realPath = path.toRealPath(LinkOption.NOFOLLOW_LINKS);
      System.out.println(realPath.toString());
      String originalPath = "d:\\data\\projects\\a-project\\..\\another-project";
      Path path1 = Paths.get(originalPath);
      Path path2 = path1.normalize();
      System.out.println("path2 = " + path2);
   }
}

Java NIO - File

Java NIO 包提供了另一个实用程序 API 名为 Files,它基本上用于使用其主要用于 Path 对象的静态方法操作文件和目录。

Java NIO package provide one more utility API named as Files which is basically used for manipulating files and directories using its static methods which mostly works on Path object.

如 Path 教程中所述,Path 接口是在 Java 7 版本的文件包中引入 Java NIO 包的。因此,此教程适用于相同的文件包。

As mentioned in Path tutorial that Path interface is introduced in Java NIO package during Java 7 version in file package.So this tutorial is for same File package.

此类完全包含对文件、目录或其他类型文件进行操作的静态方法。在大多数情况下,此处定义的方法会委托给关联的文件系统提供程序以执行文件操作。

This class consists exclusively of static methods that operate on files, directories, or other types of files.In most cases, the methods defined here will delegate to the associated file system provider to perform the file operations.

在 Files 类中定义了许多方法,也可以从 Java 文档中读取这些方法。在本教程中,我们尝试涵盖 Java NIO Files 类所有方法中的一些重要方法。

There are many methods defined in the Files class which could also be read from Java docs.In this tutorial we tried to cover some of the important methods among all of the methods of Java NIO Files class.

Important methods of Files class.

以下是 Java NIO Files 类中定义的重要方法。

Following are the important methods defined in Java NIO Files class.

  1. createFile(Path filePath, FileAttribute attrs) − Files class provides this method to create file using specified Path.

Example

package com.java.nio;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class CreateFile {
   public static void main(String[] args) {
      //initialize Path object
      Path path = Paths.get("D:file.txt");
      //create file
      try {
         Path createdFilePath = Files.createFile(path);
         System.out.println("Created a file at : "+createdFilePath);
      }
      catch (IOException e) {
         e.printStackTrace();
      }
   }
}

Output

Created a file at : D:\data\file.txt
  1. copy(InputStream in, Path target, CopyOption… options) − This method is used to copies all bytes from specified input stream to specified target file and returns number of bytes read or written as long value.LinkOption for this parameter with the following values −

Example

package com.java.nio;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;
public class WriteFile {
   public static void main(String[] args) {
      Path sourceFile = Paths.get("D:file.txt");
      Path targetFile = Paths.get("D:fileCopy.txt");
      try {
         Files.copy(sourceFile, targetFile,
         StandardCopyOption.REPLACE_EXISTING);
      }
      catch (IOException ex) {
         System.err.format("I/O Error when copying file");
      }
      Path wiki_path = Paths.get("D:fileCopy.txt");
      Charset charset = Charset.forName("ISO-8859-1");
      try {
         List<String> lines = Files.readAllLines(wiki_path, charset);
         for (String line : lines) {
            System.out.println(line);
         }
      }
      catch (IOException e) {
         System.out.println(e);
      }
   }
}

Output

To be or not to be?
  1. createDirectories(Path dir, FileAttribute<?>…​attrs) − This method is used to create directories using given path by creating all nonexistent parent directories.

  2. delete(Path path) − This method is used to deletes the file from specified path.It throws NoSuchFileException if the file is not exists at specified path or if the file is directory and it may not empty and cannot be deleted.

  3. exists(Path path) − This method is used to check if file exists at specified path and if the file exists it will return true or else it returns false.

  4. readAllBytes(Path path) − This method is used to reads all the bytes from the file at given path and returns the byte array containing the bytes read from the file.

Example

package com.java.nio;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
public class ReadFile {
   public static void main(String[] args) {
      Path wiki_path = Paths.get("D:file.txt");
      Charset charset = Charset.forName("ISO-8859-1");
      try {
         List<String> lines = Files.readAllLines(wiki_path, charset);
         for (String line : lines) {
            System.out.println(line);
         }
      }
      catch (IOException e) {
         System.out.println(e);
      }
   }
}

Output

Welcome to file.
  1. size(Path path) − This method is used to get the size of the file at specified path in bytes.

  2. write(Path path, byte[] bytes, OpenOption… options) − This method is used to writes bytes to a file at specified path.

Example

package com.java.nio;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
public class WriteFile {
   public static void main(String[] args) {
      Path path = Paths.get("D:file.txt");
      String question = "To be or not to be?";
      Charset charset = Charset.forName("ISO-8859-1");
      try {
         Files.write(path, question.getBytes());
         List<String> lines = Files.readAllLines(path, charset);
         for (String line : lines) {
            System.out.println(line);
         }
      }
      catch (IOException e) {
         System.out.println(e);
      }
   }
}

Output

To be or not to be?

Java NIO - AsynchronousFileChannel

众所周知,Java NIO 支持并发和多线程,这允许我们在同一时间同时处理不同的通道。因此,在 Java NIO 包中负责此操作的 API 是 AsynchronousFileChannel,它在 NIO 通道包下定义。因此 AsynchronousFileChannel 的限定名称为 java.nio.channels.AsynchronousFileChannel

As we know that Java NIO supports concurrency and multi-threading which allows us to deal with different channels concurrently at same time.So the API which is responsible for this in Java NIO package is AsynchronousFileChannel which is defined under NIO channels package.Hence qualified name for AsynchronousFileChannel is java.nio.channels.AsynchronousFileChannel.

AsynchronousFileChannel 类似于 NIO 的 FileChannel,但不同的是,此通道允许文件操作异步执行,而不像同步 I/O 操作中线程会进入操作并等待请求完成。因此,异步通道可供多个并发线程安全使用。

AsynchronousFileChannel is similar to that of the NIO’s FileChannel,except that this channel enables file operations to execute asynchronously unlike of synchronous I/O operation in which a thread enters into an action and waits until the request is completed.Thus asynchronous channels are safe for use by multiple concurrent threads.

在异步中,请求由线程传递给操作系统的内核以完成,而线程继续处理另一项工作。一旦内核完成工作,它就会向线程发出信号,然后线程确认信号并中断当前工作并根据需要处理 I/O 工作。

In asynchronous the request is passed by thread to the operating system’s kernel to get it done while thread continues to process another job.Once the job of kernel is done it signals the thread then the thread acknowledged the signal and interrupts the current job and processes the I/O job as needed.

为了实现并发,此通道提供了两种方法,其中一种是返回 java.util.concurrent.Future object ,另一种是将 java.nio.channels.CompletionHandler 类型对象传递给操作。

For achieving concurrency this channel provides two approaches which includes one as returning a java.util.concurrent.Future object and other is Passing to the operation an object of type java.nio.channels.CompletionHandler.

我们将逐一借助示例了解这两种方法。

We will understand both the approaches with help of examples one by one.

  1. Future Object − In this an instance of Future Interface is returned from channel.In Future interface there is get() method which returns the status of operation that is handled asynchronously on the basis of which further execution of other task could get decided.We can also check whether task is completed or not by calling its isDone method.

Example

以下示例显示如何使用 Future 对象和异步任务。

The following example shows the how to use Future object and to task asynchronously.

package com.java.nio;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class FutureObject {
   public static void main(String[] args) throws Exception {
      readFile();
   }
   private static void readFile() throws IOException, InterruptedException, ExecutionException {
      String filePath = "D:fileCopy.txt";
      printFileContents(filePath);
      Path path = Paths.get(filePath);
      AsynchronousFileChannel channel =AsynchronousFileChannel.open(path, StandardOpenOption.READ);
      ByteBuffer buffer = ByteBuffer.allocate(400);
      Future<Integer> result = channel.read(buffer, 0); // position = 0
      while (! result.isDone()) {
         System.out.println("Task of reading file is in progress asynchronously.");
      }
      System.out.println("Reading done: " + result.isDone());
      System.out.println("Bytes read from file: " + result.get());
      buffer.flip();
      System.out.print("Buffer contents: ");
      while (buffer.hasRemaining()) {
         System.out.print((char) buffer.get());
      }
      System.out.println(" ");
      buffer.clear();
      channel.close();
   }
   private static void printFileContents(String path) throws IOException {
      FileReader fr = new FileReader(path);
      BufferedReader br = new BufferedReader(fr);
      String textRead = br.readLine();
      System.out.println("File contents: ");
      while (textRead != null) {
         System.out.println("     " + textRead);
         textRead = br.readLine();
      }
   fr.close();
   br.close();
   }
}

Output

File contents:
   To be or not to be?
   Task of reading file is in progress asynchronously.
   Task of reading file is in progress asynchronously.
   Reading done: true
   Bytes read from file: 19
   Buffer contents: To be or not to be?
  1. Completion Handler − This approach is pretty simple as in this we uses CompletionHandler interface and overrides its two methods one is completed() method which is invoked when the I/O operation completes successfully and other is failed() method which is invoked if the I/O operations fails.In this a handler is created for consuming the result of an asynchronous I/O operation as once a task is completed then only the handler has functions that are executed.

Example

以下示例显示如何使用 CompletionHandler 进行异步任务。

The following example shows the how to use CompletionHandler to task asynchronously.

package com.java.nio;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class CompletionHandlerDemo {
   public static void main (String [] args) throws Exception {
      writeFile();
   }
   private static void writeFile() throws IOException {
      String input = "Content to be written to the file.";
      System.out.println("Input string: " + input);
      byte [] byteArray = input.getBytes();
      ByteBuffer buffer = ByteBuffer.wrap(byteArray);
      Path path = Paths.get("D:fileCopy.txt");
      AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
      CompletionHandler handler = new CompletionHandler() {
         @Override
         public void completed(Object result, Object attachment) {
            System.out.println(attachment + " completed and " + result + " bytes are written.");
         }
         @Override
         public void failed(Throwable exc, Object attachment) {
            System.out.println(attachment + " failed with exception:");
            exc.printStackTrace();
         }
      };
      channel.write(buffer, 0, "Async Task", handler);
      channel.close();
      printFileContents(path.toString());
   }
   private static void printFileContents(String path) throws IOException {
      FileReader fr = new FileReader(path);
      BufferedReader br = new BufferedReader(fr);
      String textRead = br.readLine();
      System.out.println("File contents: ");
      while (textRead != null) {
         System.out.println("     " + textRead);
         textRead = br.readLine();
      }
      fr.close();
      br.close();
   }
}

Output

Input string: Content to be written to the file.
Async Task completed and 34 bytes are written.
File contents:
Content to be written to the file.

Java NIO - CharSet

在 Java 中,每个字符都都有一个由 JVM 内部处理的明确定义的 unicode 代码单元。因此,Java NIO 包定义了一个名为 Charset 的抽象类,该类主要用于编码和解码字符集和 unicode。

In Java for every character there is a well defined unicode code units which is internally handled by JVM.So Java NIO package defines an abstract class named as Charset which is mainly used for encoding and decoding of charset and UNICODE.

Standard charsets

下面给出了 Java 中支持的 Charset。

The supported Charset in java are given below.

  1. US-ASCII − Seven bit ASCII characters.

  2. ISO-8859-1 − ISO Latin alphabet.

  3. UTF-8 − This is 8 bit UCS transformation format.

  4. UTF-16BE − This is 16 bit UCS transformation format with big endian byte order.

  5. UTF-16LE − This is 16 bit UCS transformation with little endian byte order.

  6. UTF-16 − 16 bit UCS transformation format.

Important methods of Charset class

  1. forName() − This method creates a charset object for the given charset name.The name can be canonical or an alias.

  2. displayName() − This method returns the canonical name of given charset.

  3. canEncode() − This method checks whether the given charset supports encoding or not.

  4. decode() − This method decodes the string of a given charset into charbuffer of Unicode charset.

  5. encode() − This method encodes charbuffer of unicode charset into the byte buffer of given charset.

Example

以下示例说明了 Charset 类的重要方法。

Following example illustrate important methods of Charset class.

package com.java.nio;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
public class CharsetExample {
   public static void main(String[] args) {
      Charset charset = Charset.forName("US-ASCII");
      System.out.println(charset.displayName());
      System.out.println(charset.canEncode());
      String str = "Demo text for conversion.";
      //convert byte buffer in given charset to char buffer in unicode
      ByteBuffer byteBuffer = ByteBuffer.wrap(str.getBytes());
      CharBuffer charBuffer = charset.decode(byteBuffer);
      //convert char buffer in unicode to byte buffer in given charset
      ByteBuffer newByteBuffer = charset.encode(charBuffer);
      while(newbb.hasRemaining()){
         char ch = (char) newByteBuffer.get();
         System.out.print(ch);
      }
      newByteBuffer.clear();
   }
}

Output

US-ASCII
Demo text for conversion.

Java NIO - FileLock

众所周知,Java NIO 支持并发和多线程,这使它能够同时处理在多个文件上运行的多个线程。但在某些情况下,我们需要文件不被任何线程共享,并且无法访问。

As we know that Java NIO supports concurrency and multi threading which enables it to deal with the multiple threads operating on multiple files at same time.But in some cases we require that our file would not get share by any of thread and get non accessible.

对于此类要求,NIO 再次提供了一个称为 FileLock 的 API,用于对整个文件或文件的一部分提供锁,以便文件或其部分不会被共享或访问。

For such requirement NIO again provides an API known as FileLock which is used to provide lock over whole file or on a part of file,so that file or its part doesn’t get shared or accessible.

为了提供或应用此类锁,我们必须使用 FileChannel 或 AsynchronousFileChannel,它们为此目的提供了两个方法,分别是 lock()tryLock()。提供的锁可以是两种类型之一:

in order to provide or apply such lock we have to use FileChannel or AsynchronousFileChannel,which provides two methods lock() and *tryLock()*for this purpose.The lock provided may be of two types −

  1. Exclusive Lock − An exclusive lock prevents other programs from acquiring an overlapping lock of either type.

  2. Shared Lock − A shared lock prevents other concurrently-running programs from acquiring an overlapping exclusive lock, but does allow them to acquire overlapping shared locks.

用于获取文件锁的方法:

Methods used for obtaining lock over file −

  1. lock() − This method of FileChannel or AsynchronousFileChannel acquires an exclusive lock over a file associated with the given channel.Return type of this method is FileLock which is further used for monitoring the obtained lock.

  2. lock(long position, long size, boolean shared) − This method again is the overloaded method of lock method and is used to lock a particular part of a file.

  3. tryLock() − This method return a FileLock or a null if the lock could not be acquired and it attempts to acquire an explicitly exclusive lock on this channel’s file.

  4. tryLock(long position, long size, boolean shared) − This method attempts to acquires a lock on the given region of this channel’s file which may be an exclusive or of shared type.

Methods of FileLock Class

  1. acquiredBy() − This method returns the channel on whose file lock was acquired.

  2. position() − This method returns the position within the file of the first byte of the locked region.A locked region need not be contained within, or even overlap, the actual underlying file, so the value returned by this method may exceed the file’s current size.

  3. size() − This method returns the size of the locked region in bytes.A locked region need not be contained within, or even overlap, the actual underlying file, so the value returned by this method may exceed the file’s current size.

  4. isShared() − This method is used to determine that whether lock is shared or not.

  5. overlaps(long position,long size) − This method tells whether or not this lock overlaps the given lock range.

  6. isValid() − This method tells whether or not the obtained lock is valid.A lock object remains valid until it is released or the associated file channel is closed, whichever comes first.

  7. release() − Releases the obtained lock.If the lock object is valid then invoking this method releases the lock and renders the object invalid. If this lock object is invalid then invoking this method has no effect.

  8. close() − This method invokes the release() method. It was added to the class so that it could be used in conjunction with the automatic resource management block construct.

Example to demonstrate file lock.

下面的示例创建文件锁并在其中写入内容

Following example create lock over a file and write content to it

package com.java.nio;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class FileLockExample {
   public static void main(String[] args) throws IOException {
      String input = "Demo text to be written in locked mode.";
      System.out.println("Input string to the test file is: " + input);
      ByteBuffer buf = ByteBuffer.wrap(input.getBytes());
      String fp = "D:file.txt";
      Path pt = Paths.get(fp);
      FileChannel channel = FileChannel.open(pt, StandardOpenOption.WRITE,StandardOpenOption.APPEND);
      channel.position(channel.size() - 1); // position of a cursor at the end of file
      FileLock lock = channel.lock();
      System.out.println("The Lock is shared: " + lock.isShared());
      channel.write(buf);
      channel.close(); // Releases the Lock
      System.out.println("Content Writing is complete. Therefore close the channel and release the lock.");
      PrintFileCreated.print(fp);
   }
}
package com.java.nio;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class PrintFileCreated {
   public static void print(String path) throws IOException {
      FileReader filereader = new FileReader(path);
      BufferedReader bufferedreader = new BufferedReader(filereader);
      String tr = bufferedreader.readLine();
      System.out.println("The Content of testout.txt file is: ");
      while (tr != null) {
         System.out.println("    " + tr);
         tr = bufferedreader.readLine();
      }
   filereader.close();
   bufferedreader.close();
   }
}

Output

Input string to the test file is: Demo text to be written in locked mode.
The Lock is shared: false
Content Writing is complete. Therefore close the channel and release the lock.
The Content of testout.txt file is:
To be or not to be?Demo text to be written in locked mode.