Java NIO - Hướng dẫn nhanh

Gói Java.nio đã được giới thiệu trong java 1.4. Ngược lại với java I / O trong java NIO, luồng dữ liệu hướng kênh và đệm cho các hoạt động I / O được giới thiệu, do đó cung cấp khả năng thực thi nhanh hơn và hiệu suất tốt hơn.

Ngoài ra, NIO API cung cấp các bộ chọn giới thiệu chức năng lắng nghe nhiều kênh cho các sự kiện IO theo cách không đồng bộ hoặc không chặn. Ở NIO, các hoạt động I / O tốn nhiều thời gian nhất bao gồm việc lấp đầy và thoát bộ đệm vào hệ điều hành, làm tăng tốc độ.

Các phần tóm tắt trung tâm của các API NIO như sau:

  • Bộ đệm, là vùng chứa dữ liệu, bảng mã và bộ giải mã và bộ mã hóa liên quan của chúng, dịch giữa byte và ký tự Unicode.

  • Các loại kênh khác nhau, biểu thị các kết nối đến các thực thể có khả năng thực hiện các hoạt động I / O

  • Các bộ chọn và các phím chọn, cùng với các kênh có thể chọn xác định một cơ sở I / O đa kênh, không chặn.

Phần này hướng dẫn bạn cách tải xuống và thiết lập Java trên máy tính của bạn. Vui lòng làm theo các bước sau để thiết lập môi trường.

Java SE có sẵn miễn phí từ liên kết Tải xuống Java . Vì vậy, bạn tải xuống một phiên bản dựa trên hệ điều hành của bạn.

Làm theo hướng dẫn để tải xuống java và chạy .exeđể cài đặt Java trên máy của bạn. Khi bạn đã cài đặt Java trên máy của mình, bạn sẽ cần đặt các biến môi trường để trỏ đến các thư mục cài đặt chính xác -

Thiết lập đường dẫn cho windows 2000 / XP

Giả sử bạn đã cài đặt Java trong thư mục c: \ Program Files \ java \ jdk -

  • Nhấp chuột phải vào 'Máy tính của tôi' và chọn 'Thuộc tính'.

  • Nhấp vào nút 'Biến môi trường' trong tab 'Nâng cao'.

  • Bây giờ thay đổi biến 'Đường dẫn' để nó cũng chứa đường dẫn đến tệp thực thi Java. Ví dụ, nếu đường dẫn hiện được đặt thành 'C: \ WINDOWS \ SYSTEM32', thì hãy thay đổi đường dẫn của bạn thành 'C: \ WINDOWS \ SYSTEM32; c: \ Program Files \ java \ jdk \ bin'.

Thiết lập đường dẫn cho windows 95/98 / ME

Giả sử bạn đã cài đặt Java trong thư mục c: \ Program Files \ java \ jdk -

  • Chỉnh sửa tệp 'C: \ autoexec.bat' và thêm dòng sau vào cuối:
    'SET PATH =% PATH%; C: \ Program Files \ java \ jdk \ bin'

Thiết lập đường dẫn cho Linux, UNIX, Solaris, FreeBSD

Biến môi trường PATH phải được đặt để trỏ đến nơi các tệp nhị phân java đã được cài đặt. Tham khảo tài liệu shell của bạn nếu bạn gặp khó khăn khi thực hiện việc này.

Ví dụ, nếu bạn sử dụng bash làm trình bao, thì bạn sẽ thêm dòng sau vào cuối '.bashrc: export PATH = / path / to / java: $ PATH'

Trình chỉnh sửa Java phổ biến

Để viết các chương trình java của bạn, bạn sẽ cần một trình soạn thảo văn bản. Thậm chí có nhiều IDE phức tạp hơn có sẵn trên thị trường. Nhưng hiện tại, bạn có thể xem xét một trong những điều sau:

  • Notepad - Trên máy Windows, bạn có thể sử dụng bất kỳ trình soạn thảo văn bản đơn giản nào như Notepad (Khuyến nghị cho hướng dẫn này), TextPad.

  • Netbeans - là một IDE Java là mã nguồn mở và miễn phí có thể được tải xuống từ http://www.netbeans.org/index.html.

  • Eclipse - cũng là một IDE java được phát triển bởi cộng đồng nguồn mở eclipse và có thể được tải xuống từ https://www.eclipse.org/.

Như chúng ta biết rằng java NIO được giới thiệu để cải tiến cho API java IO thông thường. Các cải tiến chính giúp NIO hiệu quả hơn IO là mô hình luồng dữ liệu kênh được sử dụng trong NIO và sử dụng hệ điều hành cho các tác vụ IO thông thường.

Sự khác biệt giữa Java NIO và Java IO có thể được giải thích như sau:

  • Như đã đề cập trong bài trước trong bộ đệm NIO và luồng dữ liệu hướng kênh cho các hoạt động I / O cung cấp tốc độ thực thi nhanh hơn và hiệu suất tốt hơn so với IO.Ngoài ra, NIO còn sử dụng hệ điều hành cho các tác vụ I / O thông thường, một lần nữa làm cho nó hiệu quả hơn.

  • Một khía cạnh khác của sự khác biệt giữa NIO và IO là IO này sử dụng luồng dữ liệu dòng, tức là thêm một byte tại một thời điểm và dựa vào việc chuyển đổi các đối tượng dữ liệu thành byte và ngược lại trong khi NIO xử lý các khối dữ liệu là các phần của byte.

  • Trong java các đối tượng luồng IO là một chiều trong khi trong NIO các kênh là hai chiều nghĩa là một kênh có thể được sử dụng cho cả việc đọc và ghi dữ liệu.

  • Luồng dữ liệu sắp xếp hợp lý trong IO không cho phép di chuyển qua lại trong dữ liệu. Nếu trong trường hợp cần di chuyển qua lại trong dữ liệu được đọc từ một luồng cần phải lưu trữ nó vào bộ đệm trước. Trong trường hợp NIO, chúng tôi sử dụng định hướng bộ đệm cho phép truy cập dữ liệu qua lại mà không cần bộ nhớ đệm.

  • NIO API cũng hỗ trợ đa luồng để dữ liệu có thể được đọc và ghi không đồng bộ theo cách mà trong khi thực hiện các hoạt động IO, luồng hiện tại không bị chặn. Điều này một lần nữa làm cho nó hiệu quả hơn so với API java IO thông thường.

  • Khái niệm về đa luồng được giới thiệu với sự ra đời của Selectors trong java NIO cho phép nghe nhiều kênh cho các sự kiện IO theo cách không đồng bộ hoặc không chặn.

  • Đa luồng trong NIO làm cho nó Không bị chặn có nghĩa là luồng chỉ được yêu cầu đọc hoặc ghi khi có dữ liệu, nếu không luồng có thể được sử dụng trong tác vụ khác trong thời gian bình thường. Nhưng điều này không khả thi trong trường hợp IO java thông thường vì không có đa luồng. được hỗ trợ trong đó khiến nó trở thành Chặn.

  • NIO cho phép quản lý nhiều kênh chỉ bằng một luồng duy nhất, nhưng cái giá phải trả là việc phân tích dữ liệu có thể phức tạp hơn một chút so với khi đọc dữ liệu từ luồng chặn trong trường hợp java IO. Vì vậy, trong trường hợp cần ít kết nối hơn với băng thông rất cao. với việc gửi nhiều dữ liệu cùng một lúc, trong trường hợp này, API IO của java có thể là phù hợp nhất.

Sự miêu tả

Như tên cho thấy, kênh được sử dụng làm trung bình của luồng dữ liệu từ đầu này đến đầu kia, ở đây trong java kênh NIO hoạt động giống nhau giữa bộ đệm và một thực thể ở đầu kia, nói cách khác, kênh được sử dụng để đọc dữ liệu vào bộ đệm và cũng ghi dữ liệu từ bộ đệm.

Không giống như từ các luồng được sử dụng trong các kênh Java IO thông thường có hai cách tức là có thể đọc và ghi. Kênh Java NIO hỗ trợ luồng dữ liệu không đồng bộ cả ở chế độ chặn và không chặn.

Triển khai Kênh

Kênh Java NIO được triển khai chủ yếu trong các lớp sau:

  • FileChannel- Để đọc dữ liệu từ tệp chúng ta sử dụng kênh tệp. Đối tượng của kênh tệp chỉ có thể được tạo bằng cách gọi phương thức getChannel () trên đối tượng tệp vì chúng ta không thể tạo đối tượng tệp trực tiếp.

  • DatagramChannel - Kênh datagram có thể đọc và ghi dữ liệu qua mạng thông qua UDP (User Datagram Protocol). Đối tượng của DataGramchannel có thể được tạo bằng phương pháp gốc.

  • SocketChannel- Kênh SocketChannel có thể đọc và ghi dữ liệu qua mạng thông qua TCP (Transmission Control Protocol). Nó cũng sử dụng các phương thức gốc để tạo đối tượng mới.

  • ServerSocketChannel- ServerSocketChannel đọc và ghi dữ liệu qua các kết nối TCP, giống như một máy chủ web. Đối với mỗi kết nối đến, một SocketChannel được tạo.

Thí dụ

Ví dụ sau đây đọc từ một tệp văn bản từ C:/Test/temp.txt và in nội dung ra bảng điều khiển.

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();
   }
}

Đầu ra

Hello World!

Sự miêu tả

Như đã đề cập, việc triển khai FileChannel của kênh Java NIO được giới thiệu để truy cập các thuộc tính dữ liệu meta của tệp bao gồm tạo, sửa đổi, kích thước, v.v. Cùng với đó, các Kênh tệp này là đa luồng, điều này một lần nữa làm cho Java NIO hiệu quả hơn Java IO.

Nói chung, chúng ta có thể nói rằng FileChannel là một kênh được kết nối với một tệp mà qua đó bạn có thể đọc dữ liệu từ tệp và ghi dữ liệu vào tệp. Đặc điểm quan trọng khác của FileChannel là nó không thể được đặt ở chế độ không chặn và luôn chạy ở chế độ chặn.

Chúng tôi không thể lấy đối tượng kênh tệp trực tiếp, Đối tượng kênh tệp được lấy bằng cách -

  • getChannel() - phương thức trên bất kỳ FileInputStream, FileOutputStream hoặc RandomAccessFile nào.

  • open() - phương thức của kênh Tệp mà theo mặc định sẽ mở kênh.

Kiểu đối tượng của kênh Tệp phụ thuộc vào kiểu lớp được gọi từ quá trình tạo đối tượng, tức là nếu đối tượng được tạo bằng cách gọi phương thức getchannel của FileInputStream thì kênh Tệp được mở để đọc và sẽ ném NonWlikeChannelException trong trường hợp cố gắng ghi vào nó.

Thí dụ

Ví dụ sau đây cho thấy cách đọc và ghi dữ liệu từ Java NIO FileChannel.

Ví dụ sau đây đọc từ một tệp văn bản từ C:/Test/temp.txt và in nội dung ra bảng điều khiển.

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();
   }
}

Đầu ra

Hello World! Welcome to TutorialsPoint

Java NIO Datagram được sử dụng như một kênh có thể gửi và nhận các gói UDP qua một giao thức ít kết nối hơn. Kênh datagram mặc định bị chặn trong khi nó có thể được sử dụng ở chế độ không chặn. Để làm cho nó không bị chặn, chúng ta có thể sử dụng configBlocking ( false), có thể mở kênhataGram bằng cách gọi một trong các phương thức tĩnh có tên là open() cũng có thể lấy địa chỉ IP làm tham số để nó có thể được sử dụng cho nhiều lần truyền.

Theo mặc định, kênh Datagram của FileChannel không được kết nối để làm cho nó được kết nối, chúng ta phải gọi phương thức connect () của nó một cách rõ ràng. Tuy nhiên, kênh datagram không cần được kết nối để sử dụng các phương thức gửi và nhận trong khi nó phải được kết nối để sử dụng các phương thức đọc và ghi, vì các phương thức đó không chấp nhận hoặc trả về địa chỉ socket.

Chúng ta có thể kiểm tra trạng thái kết nối của kênh datagram bằng cách gọi isConnected() Sau khi được kết nối, một kênh sơ đồ vẫn được kết nối cho đến khi nó bị ngắt kết nối hoặc bị đóng. Các kênh sơ đồ là luồng an toàn và hỗ trợ đồng thời đa luồng và đồng thời.

Các phương pháp quan trọng của kênh datagram

  • bind(SocketAddress local) - Phương thức này được sử dụng để liên kết socket của kênh datagram với địa chỉ cục bộ được cung cấp làm tham số cho phương thức này.

  • connect(SocketAddress remote) - Phương pháp này được sử dụng để kết nối ổ cắm với địa chỉ từ xa.

  • disconnect() - Phương pháp này được sử dụng để ngắt kết nối ổ cắm với địa chỉ từ xa.

  • getRemoteAddress() - Phương thức này trả về địa chỉ của vị trí từ xa mà ổ cắm của kênh được kết nối.

  • isConnected() - Như đã đề cập, phương thức này trả về trạng thái kết nối của kênh datagram tức là nó đã được kết nối hay chưa.

  • open() and open(ProtocolFamily family) - Phương thức mở được sử dụng để mở một kênh datagram cho một địa chỉ trong khi phương thức mở tham số hóa mở kênh cho nhiều địa chỉ được biểu diễn dưới dạng họ giao thức.

  • read(ByteBuffer dst) - Phương thức này dùng để đọc dữ liệu từ bộ đệm đã cho thông qua kênh datagram.

  • receive(ByteBuffer dst) - Phương thức này được sử dụng để nhận datagram qua kênh này.

  • send(ByteBuffer src, SocketAddress target) - Phương thức này được sử dụng để gửi datagram qua kênh này.

Thí dụ

Ví dụ sau đây cho thấy cách gửi dữ liệu từ Java NIO DataGramChannel.

Máy chủ: 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();
   }
}

Đầu ra

Server Started: localhost/127.0.0.1:8989

Máy khách: 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();
   }
}

Đầu ra

Chạy máy khách sẽ in đầu ra sau trên máy chủ.

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

Kênh ổ cắm Java NIO là một kênh loại có thể lựa chọn có nghĩa là nó có thể được ghép bằng bộ chọn, được sử dụng cho các ổ cắm kết nối luồng dữ liệu định hướng luồng. open() phương thức, cung cấp bất kỳ ổ cắm nào tồn tại từ trước chưa có sẵn. Kênh ổ cắm được tạo bằng cách gọi phương thức mở nhưng chưa được kết nối. Để kết nối kênh ổ cắm connect() Một điểm cần được đề cập ở đây là nếu kênh không được kết nối và bất kỳ thao tác I / O nào được cố gắng thực hiện thì NotYetConnectedException sẽ được đưa ra bởi kênh này. Vì vậy, người ta phải đảm bảo rằng kênh được kết nối trước khi thực hiện bất kỳ IO nào Hoạt động. Khi kênh được kết nối, nó vẫn được kết nối cho đến khi nó bị đóng. Trạng thái của kênh ổ cắm có thể được xác định bằng cách gọi nó isConnected phương pháp.

Kết nối của kênh ổ cắm có thể được hoàn thành bằng cách gọi finishConnect() Có thể xác định hoạt động kết nối đang diễn ra hay không bằng cách gọi phương thức isConnectionPending. Bởi vì kênh socket mặc định hỗ trợ kết nối không chặn, đồng thời hỗ trợ tắt không đồng bộ, tương tự như hoạt động đóng không đồng bộ được chỉ định trong lớp Kênh.

Các kênh ổ cắm an toàn để sử dụng bởi nhiều luồng đồng thời. Chúng hỗ trợ đọc và ghi đồng thời, mặc dù nhiều nhất một luồng có thể đang đọc và nhiều nhất một luồng có thể đang viết bất kỳ lúc nào. Các phương thức connect và finishConnect được đồng bộ hóa lẫn nhau với nhau và nỗ lực bắt đầu thao tác đọc hoặc ghi khi đang thực hiện lệnh gọi một trong các phương thức này sẽ bị chặn cho đến khi lệnh gọi đó hoàn tất.

Các phương pháp quan trọng của kênh Socket

  • bind(SocketAddress local) - Phương thức này được sử dụng để liên kết kênh ổ cắm với địa chỉ cục bộ được cung cấp làm tham số cho phương thức này.

  • connect(SocketAddress remote) - Phương pháp này được sử dụng để kết nối ổ cắm với địa chỉ từ xa.

  • finishConnect() - Phương pháp này được sử dụng để kết thúc quá trình kết nối một kênh ổ cắm.

  • getRemoteAddress() - Phương thức này trả về địa chỉ của vị trí từ xa mà ổ cắm của kênh được kết nối.

  • isConnected() - Như đã đề cập, phương pháp này trả về trạng thái kết nối của kênh ổ cắm, tức là nó đã được kết nối hay chưa.

  • open() and open((SocketAddress remote) - Phương thức mở được sử dụng để mở một kênh ổ cắm cho không có địa chỉ xác định trong khi phương thức mở được tham số hóa mở kênh cho địa chỉ từ xa được chỉ định và cũng kết nối với nó. Phương thức tiện lợi này hoạt động như thể bằng cách gọi phương thức open (), gọi phương thức kết nối dựa trên kết quả kênh ổ cắm, chuyển nó từ xa, và sau đó trả lại kênh đó.

  • read(ByteBuffer dst) - Phương pháp này được sử dụng để đọc dữ liệu từ bộ đệm đã cho thông qua kênh socket.

  • isConnectionPending() - Phương thức này cho biết hoạt động kết nối đang diễn ra trên kênh này hay không.

Thí dụ

Ví dụ sau đây cho thấy cách gửi dữ liệu từ Java NIO SocketChannel.

C: /Test/temp.txt

Hello World!

Máy khách: 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();
   }
}

Đầu ra

Chạy máy khách sẽ không in bất cứ thứ gì cho đến khi máy chủ khởi động.

Máy chủ: 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();
   }
}

Đầu ra

Chạy máy chủ sẽ in như sau.

Connection Set:  /127.0.0.1:49558
File Received

Kênh ổ cắm máy chủ Java NIO lại là một kênh loại có thể lựa chọn được sử dụng cho các ổ cắm kết nối luồng dữ liệu định hướng luồng. Kênh Socket máy chủ có thể được tạo bằng cách gọi tĩnh của nó. open() phương thức, cung cấp bất kỳ socket tồn tại từ trước nào đều chưa có. Kênh Socket máy chủ được tạo bằng cách gọi phương thức mở nhưng chưa được ràng buộc. Để gắn kết kênh socket bind() phương thức được gọi.

Một điểm cần được đề cập ở đây là nếu kênh không bị ràng buộc và bất kỳ hoạt động I / O nào được cố gắng thử thì kênh này sẽ ném NotYetBoundException. Vì vậy, người ta phải đảm bảo rằng kênh đó được giới hạn trước khi thực hiện bất kỳ hoạt động IO nào.

Các kết nối đến cho kênh ổ cắm máy chủ được lắng nghe bằng cách gọi phương thức ServerSocketChannel.accept (). Khi phương thức accept () trả về, nó sẽ trả về một SocketChannel có kết nối đến. Do đó, phương thức accept () sẽ chặn cho đến khi có kết nối đến. Nếu kênh đang ở chế độ không chặn thì phương thức accept sẽ ngay lập tức trả về null nếu không có kết nối nào đang chờ xử lý. Nếu không, nó sẽ chặn vô thời hạn cho đến khi có kết nối mới hoặc xảy ra lỗi I / O.

Ổ cắm của kênh mới ban đầu không bị ràng buộc; nó phải được liên kết với một địa chỉ cụ thể thông qua một trong các phương thức liên kết của socket của nó trước khi các kết nối có thể được chấp nhận. Ngoài ra, kênh mới được tạo bằng cách gọi phương thức openServerSocketChannel của đối tượng SelectorProvider mặc định trên toàn hệ thống.

Giống như kênh socket máy chủ, kênh socket có thể đọc dữ liệu bằng cách sử dụng read()Đầu tiên bộ đệm được cấp phát. Dữ liệu đọc từ ServerSocketChannel được lưu trữ vào bộ đệm, thứ hai chúng ta gọi phương thức ServerSocketChannel.read () và nó đọc dữ liệu từ ServerSocketChannel vào bộ đệm. Giá trị nguyên của phương thức read () trả về số byte đã được ghi vào bộ đệm

Tương tự, dữ liệu có thể được ghi vào kênh ổ cắm máy chủ bằng cách sử dụng write() phương thức sử dụng bộ đệm làm tham số Thường sử dụng phương thức ghi trong vòng lặp while khi cần lặp lại phương thức write () cho đến khi Bộ đệm không còn byte nào để ghi.

Các phương pháp quan trọng của kênh Socket

  • bind(SocketAddress local) - Phương thức này được sử dụng để liên kết kênh ổ cắm với địa chỉ cục bộ được cung cấp làm tham số cho phương thức này.

  • accept() - Phương thức này được sử dụng để chấp nhận kết nối được thực hiện với ổ cắm của kênh này.

  • connect(SocketAddress remote) - Phương pháp này được sử dụng để kết nối ổ cắm với địa chỉ từ xa.

  • finishConnect() - Phương pháp này được sử dụng để kết thúc quá trình kết nối một kênh ổ cắm.

  • getRemoteAddress() - Phương thức này trả về địa chỉ của vị trí từ xa mà ổ cắm của kênh được kết nối.

  • isConnected() - Như đã đề cập, phương pháp này trả về trạng thái kết nối của kênh ổ cắm, tức là nó đã được kết nối hay chưa.

  • open() - Phương thức mở được sử dụng để mở một kênh ổ cắm không có địa chỉ cụ thể. Phương thức tiện lợi này hoạt động như thể bằng cách gọi phương thức open (), gọi phương thức kết nối trên kênh ổ cắm máy chủ kết quả, chuyển nó từ xa, rồi trả về kênh đó.

  • read(ByteBuffer dst) - Phương pháp này được sử dụng để đọc dữ liệu từ bộ đệm đã cho thông qua kênh socket.

  • setOption(SocketOption<T> name, T value) - Phương thức này đặt giá trị của một tùy chọn socket.

  • socket() - Phương pháp này truy xuất một ổ cắm máy chủ được liên kết với kênh này.

  • validOps() - Phương thức này trả về một tập hợp hoạt động xác định các hoạt động được hỗ trợ của kênh này. Các kênh ổ cắm máy chủ chỉ hỗ trợ việc chấp nhận các kết nối mới, vì vậy phương thức này trả về SelectionKey.OP_ACCEPT.

Thí dụ

Ví dụ sau đây cho thấy cách gửi dữ liệu từ Java NIO ServerSocketChannel.

C: /Test/temp.txt

Hello World!

Máy khách: 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();
   }
}

Đầu ra

Chạy máy khách sẽ không in bất cứ thứ gì cho đến khi máy chủ khởi động.

Máy chủ: 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();
   }
}

Đầu ra

Chạy máy chủ sẽ in như sau.

Connection Set:  /127.0.0.1:49558
File Received

Như chúng ta biết rằng Java NIO là một API được tối ưu hóa hơn cho các hoạt động IO dữ liệu so với API IO thông thường của Java. Một hỗ trợ bổ sung nữa mà Java NIO cung cấp là đọc / ghi dữ liệu từ / đến nhiều bộ đệm tới kênh. và hỗ trợ ghi được gọi là Phân tán và Tập hợp trong đó dữ liệu được phân tán đến nhiều bộ đệm từ một kênh duy nhất trong trường hợp dữ liệu đọc trong khi dữ liệu được thu thập từ nhiều bộ đệm vào một kênh trong trường hợp ghi dữ liệu.

Để đạt được nhiều lần đọc và ghi này từ kênh, có API ScatteringByteChannel và GatheringByteChannel mà Java NIO cung cấp để đọc và ghi dữ liệu như minh họa trong ví dụ dưới đây.

ScatteringByteChannel

Read from multiple channels - Trong điều này, chúng tôi thực hiện để đọc dữ liệu từ một kênh duy nhất vào nhiều bộ đệm, vì nhiều bộ đệm này được cấp phát và được thêm vào một mảng kiểu bộ đệm. kênh trong chuỗi các bộ đệm xuất hiện trong mảng. Khi một bộ đệm đầy, kênh sẽ chuyển sang lấp đầy bộ đệm tiếp theo.

Ví dụ sau cho thấy cách phân tán dữ liệu được thực hiện trong 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();
      }
   }
}

Đầu ra

Scattering : Len1 = 1214606444
Scattering : Len2 = 0

Cuối cùng, có thể kết luận rằng phương pháp phân tán / tập hợp trong Java NIO được giới thiệu là một phương pháp được tối ưu hóa và đa nhiệm khi được sử dụng đúng cách. Nó cho phép bạn ủy quyền cho hệ điều hành công việc khó khăn trong việc tách dữ liệu bạn đọc thành nhiều nhóm hoặc tập hợp Không còn nghi ngờ gì nữa, điều này tiết kiệm thời gian và sử dụng hệ điều hành hiệu quả hơn bằng cách tránh các bản sao bộ đệm và giảm lượng mã cần viết và gỡ lỗi.

Như chúng ta biết rằng Java NIO là một API được tối ưu hóa hơn cho các hoạt động IO dữ liệu so với API IO thông thường của Java. Một hỗ trợ bổ sung nữa mà Java NIO cung cấp là đọc / ghi dữ liệu từ / đến nhiều bộ đệm tới kênh. và hỗ trợ ghi được gọi là Phân tán và Tập hợp trong đó dữ liệu được phân tán đến nhiều bộ đệm từ một kênh duy nhất trong trường hợp dữ liệu đọc trong khi dữ liệu được thu thập từ nhiều bộ đệm vào một kênh trong trường hợp ghi dữ liệu.

Để đạt được nhiều lần đọc và ghi này từ kênh, có API ScatteringByteChannel và GatheringByteChannel mà Java NIO cung cấp để đọc và ghi dữ liệu như minh họa trong ví dụ dưới đây.

GatheringByteChannel

write to multiple channels - Trong phần này, chúng tôi thực hiện để ghi dữ liệu từ nhiều bộ đệm vào một kênh duy nhất, đối với điều này một lần nữa, nhiều bộ đệm được cấp phát và được thêm vào một mảng kiểu bộ đệm. từ nhiều bộ đệm trong chuỗi, bộ đệm xuất hiện trong mảng. Một điểm cần nhớ ở đây là chỉ dữ liệu giữa vị trí và giới hạn của bộ đệm được ghi.

Ví dụ sau cho thấy cách thu thập dữ liệu được thực hiện trong 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();
      }
   }
}

Đầu ra

Gathering : Len1 = 24
Gathering : Len2 = 25

Cuối cùng, có thể kết luận rằng phương pháp phân tán / tập hợp trong Java NIO được giới thiệu là một phương pháp được tối ưu hóa và đa nhiệm khi được sử dụng đúng cách. Nó cho phép bạn ủy quyền cho hệ điều hành công việc khó khăn trong việc tách dữ liệu bạn đọc thành nhiều nhóm hoặc tập hợp Không còn nghi ngờ gì nữa, điều này tiết kiệm thời gian và sử dụng hệ điều hành hiệu quả hơn bằng cách tránh các bản sao bộ đệm và giảm lượng mã cần viết và gỡ lỗi.

Bộ đệm trong Java NIO có thể được coi như một đối tượng đơn giản hoạt động như một vùng chứa các khối dữ liệu có kích thước cố định có thể được sử dụng để ghi dữ liệu vào kênh hoặc đọc dữ liệu từ kênh để bộ đệm hoạt động như điểm cuối của các kênh.

Nó cung cấp tập hợp các phương pháp giúp thuận tiện hơn khi xử lý khối bộ nhớ để đọc và ghi dữ liệu vào và từ các kênh.

Bộ đệm làm cho gói NIO hiệu quả hơn và nhanh hơn so với IO cổ điển vì trong trường hợp dữ liệu IO được xử lý ở dạng luồng không hỗ trợ luồng dữ liệu không đồng bộ và đồng thời. Ngoài ra IO không cho phép thực thi dữ liệu theo đoạn hoặc nhóm byte .

Các tham số chính xác định bộ đệm Java NIO có thể được định nghĩa là:

  • Capacity - Số lượng dữ liệu / byte tối đa có thể được lưu trữ trong Bộ đệm. Dung lượng của bộ đệm không thể bị thay đổi. Khi bộ đệm đầy, nó nên được xóa trước khi ghi vào nó.

  • Limit - Giới hạn có nghĩa là theo chế độ của Bộ đệm tức là trong chế độ ghi của Bộ đệm Giới hạn bằng dung lượng có nghĩa là dữ liệu tối đa có thể được ghi trong bộ đệm. Trong khi ở chế độ đọc của bộ đệm, Giới hạn có nghĩa là giới hạn số lượng dữ liệu có thể được đọc từ Buffer.

  • Position - Trỏ đến vị trí hiện tại của con trỏ trong bộ đệm. Ban đầu được đặt là 0 tại thời điểm tạo bộ đệm hay nói cách khác nó là chỉ mục của phần tử tiếp theo sẽ được đọc hoặc ghi sẽ được cập nhật tự động bởi get () và put ( ) các phương pháp.

  • Mark - Đánh dấu đánh dấu vị trí trong bộ đệm. Khi phương thức mark () được gọi là vị trí hiện tại được ghi lại và khi reset () được gọi là vị trí đã đánh dấu được khôi phục.

Loại đệm

Bộ đệm Java NIO có thể được phân loại theo các biến thể sau trên cơ sở các kiểu dữ liệu mà bộ đệm xử lý:

  • ByteBuffer
  • MappedByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

Các phương pháp quan trọng của Buffer

Như đã đề cập ở trên, Buffer hoạt động như một đối tượng bộ nhớ, cung cấp tập hợp các phương thức giúp xử lý khối bộ nhớ thuận tiện hơn. Tiếp theo là các phương thức quan trọng của Buffer -

  • allocate(int capacity) - Phương thức này được sử dụng để cấp phát một bộ đệm mới với dung lượng là tham số. Phương thứcllocate ném IllegalArgumentException trong trường hợp dung lượng truyền vào là một số nguyên âm.

  • read() and put() - phương thức đọc của kênh được sử dụng để ghi dữ liệu từ kênh vào bộ đệm trong khi put là phương thức bộ đệm được sử dụng để ghi dữ liệu vào bộ đệm.

  • flip() - Phương thức lật chuyển chế độ của Bộ đệm từ chế độ ghi sang chế độ đọc, nó cũng đặt vị trí trở lại 0 và đặt giới hạn cho vị trí tại thời điểm ghi.

  • write() and get() - Phương thức ghi của kênh được sử dụng để ghi dữ liệu từ bộ đệm sang kênh trong khi get là phương thức của bộ đệm được sử dụng để đọc dữ liệu từ bộ đệm.

  • rewind() - Phương thức tua lại được sử dụng khi cần đọc lại vì nó đặt vị trí trở về 0 và không làm thay đổi giá trị của giới hạn.

  • clear() and compact() - rõ ràng và nhỏ gọn cả hai phương pháp được sử dụng để tạo bộ đệm từ chế độ đọc sang ghi.clear() Phương thức làm cho vị trí bằng 0 và giới hạn bằng với dung lượng, trong phương pháp này, dữ liệu trong bộ đệm không bị xóa chỉ có các điểm đánh dấu được khởi tạo lại.

    Mặt khác compact() phương thức được sử dụng khi vẫn còn một số dữ liệu chưa đọc và chúng tôi vẫn sử dụng chế độ ghi của bộ đệm trong trường hợp này phương thức compact sao chép tất cả dữ liệu chưa đọc vào đầu bộ đệm và đặt vị trí thành ngay sau phần tử chưa đọc cuối cùng. đặt thành công suất.

  • mark() and reset() - Phương pháp đánh dấu như tên gợi ý được sử dụng để đánh dấu bất kỳ vị trí cụ thể nào trong bộ đệm trong khi đặt lại làm cho vị trí trở lại vị trí đã đánh dấu.

Thí dụ

Ví dụ sau đây cho thấy việc triển khai các phương thức đã định nghĩa ở trên.

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());
   }
}

Đầu ra

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

Như chúng ta biết rằng Java NIO hỗ trợ nhiều giao dịch từ và đến các kênh và bộ đệm Vì vậy để kiểm tra một hoặc nhiều kênh NIO và xác định kênh nào đã sẵn sàng cho giao dịch dữ liệu tức là đọc hoặc ghi Java NIO cung cấp Selector.

Với Selector, chúng tôi có thể tạo một chuỗi để biết rằng kênh nào đã sẵn sàng để ghi và đọc dữ liệu và có thể xử lý kênh cụ thể đó.

Chúng ta có thể lấy thể hiện bộ chọn bằng cách gọi phương thức tĩnh của nó open()Sau khi mở bộ chọn, chúng ta phải đăng ký kênh chế độ không chặn với nó, kênh này sẽ trả về một thể hiện của SelectionKey.

SelectionKey về cơ bản là một tập hợp các thao tác có thể được thực hiện với kênh hoặc chúng ta có thể nói rằng chúng ta có thể biết trạng thái của kênh với sự trợ giúp của phím lựa chọn.

Các hoạt động chính hoặc trạng thái của kênh được biểu thị bằng phím chọn là:

  • SelectionKey.OP_CONNECT - Kênh sẵn sàng kết nối với máy chủ.

  • SelectionKey.OP_ACCEPT - Kênh sẵn sàng chấp nhận các kết nối đến.

  • SelectionKey.OP_READ - Kênh sẵn sàng đọc dữ liệu.

  • SelectionKey.OP_WRITE - Kênh sẵn sàng ghi dữ liệu.

Khóa lựa chọn nhận được sau khi đăng ký có một số phương pháp quan trọng như được đề cập bên dưới:

  • attach() - Phương pháp này được sử dụng để gắn một đối tượng với khóa Mục đích chính của việc gắn một đối tượng vào một kênh là để nhận ra cùng một kênh.

  • attachment() - Phương thức này được sử dụng để giữ lại đối tượng đính kèm khỏi kênh.

  • channel() - Phương thức này được sử dụng để lấy kênh mà khóa cụ thể được tạo.

  • selector() - Phương thức này được sử dụng để lấy bộ chọn mà khóa cụ thể được tạo.

  • isValid() - Phương thức này trả về thời tiết khóa có hợp lệ hay không.

  • isReadable() - Phương thức này cho biết kênh của khóa thời tiết đã sẵn sàng để đọc hay chưa.

  • isWritable() - Phương thức này cho biết kênh của khóa thời tiết đã sẵn sàng để ghi hay chưa.

  • isAcceptable() - Phương thức này nói rằng kênh của khóa thời tiết đã sẵn sàng để chấp nhận kết nối đến hay chưa.

  • isConnectable() - Phương pháp này kiểm tra xem kênh của khóa này đã kết thúc hay không kết thúc, hoạt động kết nối ổ cắm của nó.

  • isAcceptable() - Phương pháp này kiểm tra xem kênh của phím này đã sẵn sàng chấp nhận kết nối ổ cắm mới chưa.

  • interestOps() - Phương thức này truy xuất tập hợp quan tâm của khóa này.

  • readyOps() - Phương thức này truy xuất tập sẵn sàng là tập hợp các hoạt động mà kênh đã sẵn sàng.

Chúng ta có thể chọn một kênh từ bộ chọn bằng cách gọi phương thức tĩnh của nó select()Phương thức .Select của bộ chọn bị quá tải vì -

  • select() - Phương thức này chặn luồng hiện tại cho đến khi ít nhất một kênh sẵn sàng cho các sự kiện mà nó được đăng ký.

  • select(long timeout) - Phương thức này hoạt động tương tự như select () ngoại trừ nó chặn luồng trong thời gian chờ tối đa mili giây (tham số).

  • selectNow() - Phương thức này hoàn toàn không chặn mà trả về ngay lập tức với bất kỳ kênh nào đã sẵn sàng.

Ngoài ra, để thoát khỏi một chuỗi bị chặn gọi ra phương pháp chọn,wakeup() phương thức có thể được gọi từ cá thể selector mà sau đó luồng đang chờ bên trong select () sẽ trả về ngay lập tức.

Cuối cùng, chúng ta có thể đóng bộ chọn bằng cách gọi close() phương pháp này cũng làm mất hiệu lực của tất cả các cá thể SelectionKey đã đăng ký với Bộ chọn này cùng với việc đóng bộ chọn.

Thí dụ

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();
         }
      }
   }
}

Trong Java NIO pipe là một thành phần được sử dụng để ghi và đọc dữ liệu giữa hai luồng, chủ yếu bao gồm hai kênh chịu trách nhiệm truyền dữ liệu.

Trong số hai kênh cấu thành, một được gọi là kênh Sink chủ yếu để ghi dữ liệu và kênh khác là kênh Nguồn có mục đích chính là đọc dữ liệu từ kênh Sink.

Đồng bộ hóa dữ liệu được giữ theo thứ tự trong quá trình ghi và đọc dữ liệu vì nó phải đảm bảo rằng dữ liệu phải được đọc theo cùng một thứ tự mà nó được ghi vào Pipe.

Cần lưu ý rằng nó là luồng dữ liệu một chiều trong Pipe, tức là dữ liệu chỉ được ghi trong kênh Sink và chỉ có thể được đọc từ kênh Nguồn.

Trong Java NIO pipe được định nghĩa là một lớp trừu tượng với chủ yếu là ba phương thức, trong đó hai phương thức là trừu tượng.

Các phương thức của lớp Pipe

  • open() - Phương thức này được sử dụng để lấy một thể hiện của Pipe hoặc có thể nói đường ống được tạo ra bằng cách gọi ra phương thức này.

  • sink() - Phương thức này trả về kênh chìm của Pipe được sử dụng để ghi dữ liệu bằng cách gọi phương thức ghi của nó.

  • source() - Phương thức này trả về kênh nguồn của Pipe được sử dụng để đọc dữ liệu bằng cách gọi phương thức đọc của nó.

Thí dụ

Ví dụ sau đây cho thấy việc triển khai 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();
      }
   }
}

Đầu ra

Test Data to Check java NIO Channels Pipe.

Giả sử chúng ta có một tệp văn bản c:/test.txt, có nội dung sau. Tệp này sẽ được sử dụng làm đầu vào cho chương trình mẫu của chúng tôi.

Như tên cho thấy Đường dẫn là vị trí cụ thể của một thực thể như tệp hoặc thư mục trong hệ thống tệp để người ta có thể tìm kiếm và truy cập nó tại vị trí cụ thể đó.

Về mặt kỹ thuật của Java, Path là một giao diện được giới thiệu trong gói tệp Java NIO trong phiên bản Java 7 và là đại diện của vị trí trong hệ thống tệp cụ thể. Vì giao diện đường dẫn nằm trong gói Java NIO nên nó có tên đủ điều kiện là java .nio.file.Path.

Nói chung, đường dẫn của một thực thể có thể có hai loại một là đường dẫn tuyệt đối và đường khác là đường dẫn tương đối. Tên của cả hai đường dẫn cho thấy rằng đường dẫn tuyệt đối là địa chỉ vị trí từ gốc đến thực thể nơi nó định vị trong khi đường dẫn tương đối là địa chỉ vị trí liên quan đến một số đường dẫn khác. Đường dẫn sử dụng dấu phân cách trong định nghĩa của nó là "\" cho Windows và "/" cho hệ điều hành unix.

Để có được thể hiện của Path, chúng ta có thể sử dụng phương thức tĩnh của lớp java.nio.file.Paths get()Phương thức này chuyển đổi một chuỗi đường dẫn hoặc một chuỗi các chuỗi khi được nối với nhau tạo thành một chuỗi đường dẫn, thành một thể hiện Đường dẫn. Phương thức này cũng ném ra thời gian chạy không hợp lệ nếu các đối số được truyền chứa các ký tự không hợp lệ.

Như đã đề cập ở trên, đường dẫn tuyệt đối được truy xuất bằng cách truyền phần tử gốc và danh sách thư mục đầy đủ cần thiết để định vị tệp. Trong khi đường dẫn tương đối có thể được truy xuất bằng cách kết hợp đường dẫn cơ sở với đường dẫn tương đối. Việc truy xuất cả hai đường dẫn sẽ được minh họa trong ví dụ sau

Thí dụ

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);
   }
}

Cho đến nay chúng ta đã biết rằng giao diện đường dẫn là gì tại sao chúng ta cần nó và làm thế nào chúng ta có thể truy cập nó. Bây giờ chúng ta sẽ biết các phương thức quan trọng mà giao diện đường dẫn cung cấp cho chúng ta.

Các phương pháp quan trọng của Giao diện Đường dẫn

  • getFileName() - Trả về hệ thống tệp đã tạo đối tượng này.

  • getName() - Trả về một phần tử tên của đường dẫn này như một đối tượng Đường dẫn.

  • getNameCount() - Trả về số phần tử tên trong đường dẫn.

  • subpath() - Trả về một Đường dẫn tương đối là một dãy con của các phần tử tên của đường dẫn này.

  • getParent() - Trả về đường dẫn cha hoặc null nếu đường dẫn này không có cha.

  • getRoot() - Trả về thành phần gốc của đường dẫn này dưới dạng đối tượng Đường dẫn hoặc null nếu đường dẫn này không có thành phần gốc.

  • toAbsolutePath() - Trả về một đối tượng Path đại diện cho đường dẫn tuyệt đối của đường dẫn này.

  • toRealPath() - Trả về đường dẫn thực của một tệp hiện có.

  • toFile() - Trả về một đối tượng File đại diện cho đường dẫn này.

  • normalize() - Trả về một đường dẫn là đường dẫn này với các phần tử tên dư thừa bị loại bỏ.

  • compareTo(Path other) - So sánh hai đường dẫn trừu tượng về mặt từ vựng. Phương thức này trả về 0 nếu đối số bằng với đường dẫn này, giá trị nhỏ hơn 0 nếu đường dẫn này nhỏ hơn về mặt từ vựng hoặc giá trị lớn hơn 0 nếu đường dẫn này về mặt từ vựng lớn hơn đối số .

  • endsWith(Path other) - Kiểm tra xem đường dẫn này có kết thúc bằng đường dẫn đã cho hay không. Nếu đường dẫn đã cho có N phần tử và không có thành phần gốc và đường dẫn này có N phần tử trở lên, thì đường dẫn này kết thúc bằng đường dẫn đã cho nếu N phần tử cuối cùng của mỗi đường dẫn, bắt đầu từ phần tử xa gốc nhất, bằng nhau.

  • endsWith(String other) - Kiểm tra xem đường dẫn này có kết thúc bằng một Đường dẫn, được xây dựng bằng cách chuyển đổi chuỗi đường dẫn đã cho, theo đúng cách được chỉ định bởi phương thức endWith (Đường dẫn) hay không.

Thí dụ

Ví dụ sau minh họa các phương thức khác nhau của giao diện Path được đề cập ở trên:

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);
   }
}

Gói Java NIO cung cấp thêm một API tiện ích có tên là Tệp, về cơ bản được sử dụng để thao tác các tệp và thư mục bằng cách sử dụng các phương thức tĩnh của nó chủ yếu hoạt động trên đối tượng Path.

Như đã đề cập trong hướng dẫn Đường dẫn rằng giao diện Đường dẫn được giới thiệu trong gói Java NIO trong phiên bản Java 7 trong gói tệp. Vì vậy, hướng dẫn này dành cho cùng một gói tệp.

Lớp này chỉ bao gồm các phương thức tĩnh hoạt động trên tệp, thư mục hoặc các loại tệp khác. Trong hầu hết các trường hợp, các phương thức được định nghĩa ở đây sẽ ủy quyền cho nhà cung cấp hệ thống tệp được liên kết để thực hiện các thao tác với tệp.

Có nhiều phương thức được định nghĩa trong lớp Files cũng có thể được đọc từ tài liệu Java. Trong hướng dẫn này, chúng tôi đã cố gắng đề cập đến một số phương thức quan trọng trong số tất cả các phương thức của lớp Java NIO Files.

Các phương thức quan trọng của lớp Files.

Sau đây là các phương thức quan trọng được định nghĩa trong lớp Java NIO Files.

  • createFile(Path filePath, FileAttribute attrs) - Lớp Files cung cấp phương thức này để tạo tệp bằng đường dẫn được chỉ định.

Thí dụ

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();
      }
   }
}

Đầu ra

Created a file at : D:\data\file.txt
  • copy(InputStream in, Path target, CopyOption… options) - Phương thức này được sử dụng để sao chép tất cả các byte từ luồng đầu vào được chỉ định sang tệp đích đã chỉ định và trả về số byte được đọc hoặc ghi dưới dạng giá trị dài. Liên kếtOption cho tham số này với các giá trị sau:

    • COPY_ATTRIBUTES - sao chép các thuộc tính vào tệp mới, ví dụ: thuộc tính thời gian sửa đổi lần cuối.

    • REPLACE_EXISTING - thay thế một tệp hiện có nếu nó tồn tại.

    • NOFOLLOW_LINKS - Nếu một tệp là một liên kết tượng trưng, ​​thì bản thân liên kết, không phải mục tiêu của liên kết, sẽ được sao chép.

Thí dụ

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);
      }
   }	
}

Đầu ra

To be or not to be?
  • createDirectories(Path dir, FileAttribute<?>...attrs) - Phương pháp này được sử dụng để tạo các thư mục sử dụng đường dẫn đã cho bằng cách tạo tất cả các thư mục mẹ không tồn tại.

  • delete(Path path) - Phương thức này được sử dụng để xóa tệp khỏi đường dẫn đã chỉ định, nó ném NoSuchFileException nếu tệp không tồn tại ở đường dẫn đã chỉ định hoặc nếu tệp là thư mục và nó có thể không trống và không thể xóa được.

  • exists(Path path) - Phương thức này được sử dụng để kiểm tra xem tệp có tồn tại ở đường dẫn được chỉ định hay không và nếu tệp tồn tại, nó sẽ trả về true hoặc nếu không nó sẽ trả về false.

  • readAllBytes(Path path) - Phương thức này được sử dụng để đọc tất cả các byte từ tệp tại đường dẫn đã cho và trả về mảng byte chứa các byte đã đọc từ tệp.

Thí dụ

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);
      }
   }	
}

Đầu ra

Welcome to file.
  • size(Path path) - Phương thức này được sử dụng để lấy kích thước của tệp tại đường dẫn xác định tính bằng byte.

  • write(Path path, byte[] bytes, OpenOption… options) - Phương thức này được sử dụng để ghi các byte vào một tệp theo đường dẫn được chỉ định.

Thí dụ

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);
      }
   }
}

Đầu ra

To be or not to be?

Như chúng ta biết rằng Java NIO hỗ trợ đồng thời và đa luồng, cho phép chúng ta xử lý đồng thời các kênh khác nhau cùng một lúc. Vì vậy, API chịu trách nhiệm về điều này trong gói Java NIO là AsynchronousFileChannel được định nghĩa trong gói kênh NIO. đối với AsynchronousFileChannel là java.nio.channels.AsynchronousFileChannel.

AsynchronousFileChannel tương tự như kênh FileChannel của NIO, ngoại trừ việc kênh này cho phép các hoạt động tệp thực thi không đồng bộ không giống như hoạt động I / O đồng bộ trong đó một luồng tham gia vào một hành động và đợi cho đến khi yêu cầu được hoàn thành. bởi nhiều chủ đề đồng thời.

Trong chế độ không đồng bộ, yêu cầu được chuyển theo luồng tới nhân của hệ điều hành để hoàn thành nó trong khi luồng tiếp tục xử lý một công việc khác. Khi công việc của nhân được thực hiện, nó báo hiệu cho luồng sau đó luồng nhận tín hiệu và ngắt công việc hiện tại và xử lý I / O công việc khi cần thiết.

Để đạt được sự đồng thời, kênh này cung cấp hai cách tiếp cận bao gồm một cách tiếp cận là trả về java.util.concurrent.Future object và khác là Truyền cho hoạt động một đối tượng kiểu java.nio.channels.CompletionHandler.

Chúng tôi sẽ hiểu cả hai cách tiếp cận với sự trợ giúp của từng ví dụ một.

  • Future Object - Trong phiên bản này của Giao diện Tương lai được trả về từ kênh. Trong giao diện Tương lai có get() phương thức trả về trạng thái hoạt động được xử lý không đồng bộ trên cơ sở đó có thể quyết định việc thực thi thêm tác vụ khác. Chúng tôi cũng có thể kiểm tra xem tác vụ đã hoàn thành hay chưa bằng cách gọi nó isDone phương pháp.

Thí dụ

Ví dụ sau đây cho thấy cách sử dụng đối tượng Tương lai và tác vụ không đồng bộ.

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();
   }
}

Đầu ra

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?
  • Completion Handler -

    Cách tiếp cận này khá đơn giản vì trong giao diện này chúng tôi sử dụng giao diện CompletionHandler và ghi đè hai phương pháp của nó, một là completed() phương thức được gọi khi thao tác I / O hoàn tất thành công và phương thức khác là failed() Phương thức này được gọi nếu các hoạt động I / O không thành công. Trong đó một trình xử lý được tạo ra để sử dụng kết quả của một hoạt động I / O không đồng bộ khi một tác vụ được hoàn thành thì chỉ có trình xử lý mới có các chức năng được thực thi.

Thí dụ

Ví dụ sau đây cho thấy cách sử dụng CompletionHandler để tác vụ không đồng bộ.

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();
   }
}

Đầu ra

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.

Trong Java cho mỗi ký tự có một đơn vị mã unicode được xác định rõ ràng được xử lý bên trong bởi JVM. Vì vậy, gói Java NIO định nghĩa một lớp trừu tượng có tên là Charset chủ yếu được sử dụng để mã hóa và giải mã bảng mã và UNICODE.

Bảng mã chuẩn

Bộ mã được hỗ trợ trong java được đưa ra dưới đây.

  • US-ASCII - Bảy ký tự ASCII bit.

  • ISO-8859-1 - Bảng chữ cái Latinh ISO.

  • UTF-8 - Đây là định dạng chuyển đổi UCS 8 bit.

  • UTF-16BE - Đây là định dạng chuyển đổi UCS 16 bit với thứ tự byte cuối lớn.

  • UTF-16LE - Đây là phép biến đổi UCS 16 bit với ít thứ tự byte cuối.

  • UTF-16 - Định dạng chuyển đổi UCS 16 bit.

Các phương thức quan trọng của lớp Charset

  • forName() - Phương thức này tạo một đối tượng bộ mã cho tên bộ mã đã cho. Tên có thể là chuẩn hoặc bí danh.

  • displayName() - Phương thức này trả về tên chính tắc của tập ký tự đã cho.

  • canEncode() - Phương pháp này kiểm tra xem bộ mã đã cho có hỗ trợ mã hóa hay không.

  • decode() - Phương pháp này giải mã chuỗi của một bộ mã nhất định thành bộ đệm ký tự của bộ mã Unicode.

  • encode() - Phương pháp này mã hóa bộ đệm ký tự unicode vào bộ đệm byte của bộ ký tự đã cho.

Thí dụ

Ví dụ sau minh họa các phương thức quan trọng của lớp Charset.

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();
   }
}

Đầu ra

US-ASCII
Demo text for conversion.

Như chúng ta biết rằng Java NIO hỗ trợ đồng thời và đa luồng, cho phép nó xử lý nhiều luồng hoạt động trên nhiều tệp cùng một lúc. Nhưng trong một số trường hợp, chúng tôi yêu cầu tệp của chúng tôi sẽ không được chia sẻ bởi bất kỳ luồng nào và không thể truy cập được.

Đối với yêu cầu như vậy, NIO một lần nữa cung cấp một API được gọi là FileLock được sử dụng để cung cấp khóa trên toàn bộ tệp hoặc trên một phần của tệp, do đó tệp đó hoặc phần của nó không được chia sẻ hoặc truy cập.

Để cung cấp hoặc áp dụng khóa như vậy, chúng tôi phải sử dụng FileChannel hoặc AsynchronousFileChannel, cung cấp hai phương pháp lock()tryLock()cho mục đích này. Khóa được cung cấp có thể có hai loại -

  • Exclusive Lock - Một khóa độc quyền ngăn các chương trình khác có được một khóa chồng chéo của một trong hai loại.

  • Shared Lock - Một khóa chia sẻ ngăn các chương trình chạy đồng thời khác có được một khóa độc quyền chồng chéo, nhưng cho phép chúng có được các khóa chia sẻ chồng chéo.

Các phương pháp được sử dụng để lấy tệp khóa qua -

  • lock() - Phương thức FileChannel hoặc AsynchronousFileChannel này có được một khóa độc quyền đối với một tệp được liên kết với kênh đã cho. Loại trả về của phương pháp này là FileLock được sử dụng thêm để theo dõi khóa thu được.

  • lock(long position, long size, boolean shared) - Phương thức này lại là phương thức nạp chồng của phương thức khóa và được sử dụng để khóa một phần cụ thể của tệp.

  • tryLock() - Phương thức này trả về FileLock hoặc null nếu không thể lấy được khóa và nó cố gắng lấy được khóa độc quyền rõ ràng trên tệp của kênh này.

  • tryLock(long position, long size, boolean shared) - Phương pháp này cố gắng có được một khóa trên khu vực nhất định của tệp của kênh này, có thể là loại độc quyền hoặc loại được chia sẻ.

Phương thức của lớp FileLock

  • acquiredBy() - Phương thức này trả về kênh có khóa tệp được lấy.

  • position() - Phương thức này trả về vị trí bên trong tệp của byte đầu tiên của vùng bị khóa. Vùng bị khóa không cần phải được chứa trong hoặc thậm chí chồng chéo, tệp cơ sở thực tế, vì vậy giá trị được trả về bởi phương thức này có thể vượt quá kích thước hiện tại của tệp.

  • size() - Phương thức này trả về kích thước của vùng bị khóa tính bằng byte. Vùng bị khóa không cần phải được chứa trong hoặc thậm chí chồng chéo, tệp cơ bản thực tế, vì vậy giá trị được trả về bởi phương thức này có thể vượt quá kích thước hiện tại của tệp.

  • isShared() - Phương pháp này được sử dụng để xác định xem khóa có được chia sẻ hay không.

  • overlaps(long position,long size) - Phương thức này cho biết khóa này có chồng lên phạm vi khóa đã cho hay không.

  • isValid() - Phương thức này cho biết khóa thu được có hợp lệ hay không. Đối tượng khóa vẫn có giá trị cho đến khi nó được phát hành hoặc kênh tệp liên quan bị đóng, tùy điều kiện nào đến trước.

  • release()- Giải phóng khóa thu được Nếu đối tượng khóa hợp lệ thì việc gọi phương thức này sẽ giải phóng khóa và làm cho đối tượng không hợp lệ. Nếu đối tượng khóa này không hợp lệ thì việc gọi phương thức này không có hiệu lực.

  • close()- Phương thức này gọi phương thức release (). Nó đã được thêm vào lớp để nó có thể được sử dụng cùng với cấu trúc khối quản lý tài nguyên tự động.

Ví dụ để chứng minh khóa tệp.

Ví dụ sau tạo khóa trên một tệp và ghi nội dung vào tệp đó

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();  
   }  
}

Đầu ra

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.