Java NIO - Szybki przewodnik

Pakiet Java.nio został wprowadzony w java 1.4. W przeciwieństwie do java I / O w java NIO wprowadzono buforowy i kanałowy przepływ danych dla operacji I / O, co w rezultacie zapewnia szybsze wykonanie i lepszą wydajność.

NIO API oferuje również selektory, które wprowadzają funkcjonalność nasłuchiwania wielu kanałów dla zdarzeń IO w sposób asynchroniczny lub nieblokujący.

Główne abstrakcje API NIO są następujące:

  • Bufory, które są kontenerami na dane, zestawy znaków i związane z nimi dekodery i kodery, które dokonują translacji między bajtami i znakami Unicode.

  • Kanały różnych typów, które reprezentują połączenia z jednostkami zdolnymi do wykonywania operacji we / wy

  • Selektory i klawisze wyboru, które wraz z wybieralnymi kanałami definiują multipleksowane, nieblokujące urządzenie we / wy.

Ta sekcja zawiera instrukcje pobierania i konfigurowania oprogramowania Java na komputerze. Wykonaj poniższe czynności, aby skonfigurować środowisko.

Java SE jest dostępna bezpłatnie pod linkiem Pobierz Javę . Więc pobierasz wersję opartą na systemie operacyjnym.

Postępuj zgodnie z instrukcjami, aby pobrać java i uruchomić .exeaby zainstalować Javę na swoim komputerze. Po zainstalowaniu Javy na komputerze należałoby ustawić zmienne środowiskowe, aby wskazywały na prawidłowe katalogi instalacyjne -

Konfigurowanie ścieżki dla systemu Windows 2000 / XP

Zakładając, że zainstalowałeś Javę w katalogu c: \ Program Files \ java \ jdk -

  • Kliknij prawym przyciskiem myszy „Mój komputer” i wybierz „Właściwości”.

  • Kliknij przycisk „Zmienne środowiskowe” na karcie „Zaawansowane”.

  • Teraz zmień zmienną „Path”, tak aby zawierała również ścieżkę do pliku wykonywalnego Java. Na przykład, jeśli ścieżka jest obecnie ustawiona na „C: \ WINDOWS \ SYSTEM32”, ​​zmień ścieżkę na „C: \ WINDOWS \ SYSTEM32; c: \ Program Files \ java \ jdk \ bin”.

Utworzenie ścieżki dla Windows 95/98 / ME

Zakładając, że zainstalowałeś Javę w katalogu c: \ Program Files \ java \ jdk -

  • Edytuj plik „C: \ autoexec.bat” i dodaj na końcu następujący wiersz:
    „SET PATH =% PATH%; C: \ Program Files \ java \ jdk \ bin”

Utworzenie ścieżki dla Linux, UNIX, Solaris, FreeBSD

Zmienna środowiskowa PATH powinna być ustawiona tak, aby wskazywała, gdzie zostały zainstalowane pliki binarne Java. Zajrzyj do dokumentacji powłoki, jeśli masz z tym problem.

Na przykład, jeśli używasz bash jako powłoki, to dodasz następujący wiersz na końcu swojego '.bashrc: export PATH = / path / to / java: $ PATH'

Popularne edytory Java

Do pisania programów Java będziesz potrzebować edytora tekstu. Na rynku dostępne są jeszcze bardziej wyrafinowane IDE. Ale na razie możesz rozważyć jedną z następujących -

  • Notepad - Na komputerze z systemem Windows możesz użyć dowolnego prostego edytora tekstu, takiego jak Notatnik (zalecany w tym samouczku), TextPad.

  • Netbeans - to środowisko Java IDE o otwartym kodzie źródłowym i bezpłatne, które można pobrać z http://www.netbeans.org/index.html.

  • Eclipse - to także środowisko java IDE opracowane przez społeczność open source eclipse i można je pobrać z witryny https://www.eclipse.org/.

Jak wiemy, java NIO jest wprowadzana w celu udoskonalenia konwencjonalnego java IO API. Główne ulepszenia, które czynią NIO bardziej wydajnym niż IO to model przepływu danych kanału używany w NIO oraz wykorzystanie systemu operacyjnego do konwencjonalnych zadań IO.

Różnicę między Java NIO i Java IO można wyjaśnić następująco:

  • Jak wspomniano w poprzednim poście w Buforach NIO i przepływach danych zorientowanych na kanał dla operacji I / O, które zapewniają szybsze wykonanie i lepszą wydajność w porównaniu do IO.Również NIO używa systemu operacyjnego do konwencjonalnych zadań I / O, co ponownie czyni go bardziej wydajnym.

  • Innym aspektem różnicy między NIO i IO jest to, że to IO wykorzystuje strumień danych, tj. Jeden bajt więcej na raz i polega na konwersji obiektów danych na bajty i vice-e-versa, podczas gdy NIO zajmuje się blokami danych, które są fragmentami bajtów.

  • W Javie obiekty strumienia IO są jednokierunkowe, podczas gdy w NIO kanały są dwukierunkowe, co oznacza, że ​​kanał może być używany zarówno do odczytu, jak i zapisu danych.

  • Usprawniony przepływ danych w IO nie pozwala na poruszanie się w przód iw tył w danych, jeśli zajdzie potrzeba poruszania się w przód iw tył w danych odczytanych ze strumienia, najpierw należy je buforować w buforze, podczas gdy w przypadku NIO używamy buforowania zorientowanego co pozwala na dostęp do danych w obie strony bez potrzeby buforowania.

  • NIO API obsługuje również wielowątkowość, dzięki czemu dane mogą być odczytywane i zapisywane asynchronicznie, na przykład w taki sposób, że podczas wykonywania operacji IO bieżący wątek nie jest blokowany, co ponownie czyni go bardziej wydajnym niż konwencjonalne Java IO API.

  • Koncepcja wielowątkowości została wprowadzona wraz z wprowadzeniem Selectors w java NIO, które pozwalają na nasłuchiwanie wielu kanałów dla zdarzeń IO w sposób asynchroniczny lub nieblokujący.

  • Wielowątkowość w NIO sprawia, że ​​nie blokuje, co oznacza, że ​​wątek jest proszony o odczyt lub zapis tylko wtedy, gdy dane są dostępne, w przeciwnym razie wątek może być używany w innym zadaniu przez średni czas, ale nie jest to możliwe w przypadku konwencjonalnego wejścia / wyjścia Java, ponieważ nie ma wielowątkowości jest w nim obsługiwany, co sprawia, że ​​jest to blokowanie.

  • NIO pozwala na zarządzanie wieloma kanałami przy użyciu tylko jednego wątku, ale kosztem jest to, że parsowanie danych może być nieco bardziej skomplikowane niż w przypadku odczytu danych ze strumienia blokującego w przypadku IO java, więc w przypadku, gdy wymagana jest mniejsza liczba połączeń o bardzo dużej przepustowości przy wysyłaniu dużej ilości danych na raz, wtedy java IO API może być najlepszym rozwiązaniem.

Opis

Jak sama nazwa wskazuje, kanał jest używany jako środek przepływu danych z jednego końca na drugi. Tutaj w java NIO kanał działa tak samo między buforem a jednostką na drugim końcu, innymi słowy kanał służy do odczytu danych do bufora, a także do zapisu danych z bufora.

W przeciwieństwie do strumieni, które są używane w konwencjonalnych Java, kanały IO są dwukierunkowe, tj. Mogą zarówno czytać, jak i pisać. Kanał Java NIO obsługuje asynchroniczny przepływ danych zarówno w trybie blokowym, jak i nieblokującym.

Wdrożenia Channel

Kanał Java NIO jest realizowany przede wszystkim w następujących klasach -

  • FileChannel- Do odczytu danych z pliku wykorzystujemy kanał plików. Obiekt kanału pliku można utworzyć tylko przez wywołanie metody getChannel () na obiekcie plik, ponieważ nie możemy bezpośrednio utworzyć obiektu pliku.

  • DatagramChannel - Kanał datagramowy może odczytywać i zapisywać dane w sieci za pośrednictwem protokołu UDP (User Datagram Protocol). Obiekt DataGramchannel można utworzyć przy użyciu metod fabrycznych.

  • SocketChannel- Kanał SocketChannel może odczytywać i zapisywać dane w sieci za pośrednictwem protokołu TCP (Transmission Control Protocol). Używa również metod fabrycznych do tworzenia nowego obiektu.

  • ServerSocketChannel- ServerSocketChannel odczytuje i zapisuje dane przez połączenia TCP, tak samo jak serwer WWW. Dla każdego połączenia przychodzącego tworzony jest SocketChannel.

Przykład

Poniższy przykład odczytuje z pliku tekstowego z C:/Test/temp.txt i drukuje zawartość na konsoli.

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

Wynik

Hello World!

Opis

Jak już wspomniano, wprowadzono implementację kanału Java NIO w FileChannel w celu uzyskania dostępu do właściwości metadanych pliku, w tym tworzenia, modyfikacji, rozmiaru itp. Wraz z tym kanałami plików są wielowątkowe, co ponownie sprawia, że ​​Java NIO jest bardziej wydajna niż Java IO.

Ogólnie możemy powiedzieć, że FileChannel jest kanałem podłączonym do pliku, za pomocą którego można odczytywać dane z pliku i zapisywać dane do pliku Inną ważną cechą FileChannel jest to, że nie można go ustawić w tryb nieblokujący i zawsze działa w trybie blokowania.

Nie możemy pobrać obiektu kanału pliku bezpośrednio, obiekt kanału pliku jest uzyskiwany albo przez -

  • getChannel() - metoda na dowolnym FileInputStream, FileOutputStream lub RandomAccessFile.

  • open() - metoda kanału plików, która domyślnie otwiera kanał.

Typ obiektu kanału File zależy od typu klasy wywołanej podczas tworzenia obiektu, tj. Jeśli obiekt jest tworzony przez wywołanie metody getchannel z FileInputStream, wówczas kanał File jest otwierany do odczytu i zgłasza NonWritableChannelException w przypadku próby zapisu do niego.

Przykład

Poniższy przykład pokazuje, jak odczytywać i zapisywać dane z Java NIO FileChannel.

Poniższy przykład odczytuje z pliku tekstowego z C:/Test/temp.txt i drukuje zawartość na konsoli.

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

Wynik

Hello World! Welcome to TutorialsPoint

Java NIO Datagram jest używany jako kanał, który może wysyłać i odbierać pakiety UDP przez protokół bez połączenia.Domyślnie kanał datagramu jest blokowany, podczas gdy może być używany w trybie nieblokującym.Aby nie blokować, możemy użyć konfiguracji configureBlocking ( Kanał DataGram można otworzyć, wywołując jedną z metod statycznych o nazwie as open() który może również przyjąć adres IP jako parametr, aby można go było używać do przesyłania wielu treści.

Kanał datagramowy, podobnie jak FileChannel, nie jest domyślnie połączony, aby go połączyć, musimy jawnie wywołać jego metodę connect (), jednak kanał datagramu nie musi być połączony, aby metody wysyłania i odbierania były używane, gdy muszą być połączone w celu użycia metod odczytu i zapisu, ponieważ metody te nie akceptują ani nie zwracają adresów gniazd.

Stan połączenia kanału datagramowego możemy sprawdzić, wywołując jego isConnected() Po podłączeniu kanał datagramowy pozostaje połączony, dopóki nie zostanie odłączony lub zamknięty. Kanały datagramowe są bezpieczne wątkowo i obsługują jednocześnie wielowątkowość i współbieżność.

Ważne metody kanału datagramowego

  • bind(SocketAddress local) - Ta metoda służy do powiązania gniazda kanału datagramowego z adresem lokalnym, który jest dostarczany jako parametr tej metody.

  • connect(SocketAddress remote) - Ta metoda służy do podłączenia gniazda do zdalnego adresu.

  • disconnect() - Ta metoda służy do odłączania gniazda od zdalnego adresu.

  • getRemoteAddress() - Ta metoda zwraca adres zdalnej lokalizacji, do której jest podłączone gniazdo kanału.

  • isConnected() - Jak już wspomniano, ta metoda zwraca stan połączenia kanału datagramowego, tj. Czy jest podłączony czy nie.

  • open() and open(ProtocolFamily family) - Metoda otwarta służy do otwierania kanału datagramowego dla pojedynczego adresu, podczas gdy sparametryzowany kanał otwarty metody otwartej dla wielu adresów reprezentowanych jako rodzina protokołów.

  • read(ByteBuffer dst) - Metoda ta służy do odczytu danych z danego bufora przez kanał datagramowy.

  • receive(ByteBuffer dst) - Ta metoda służy do odbierania datagramów przez ten kanał.

  • send(ByteBuffer src, SocketAddress target) - Ta metoda służy do wysyłania datagramów przez ten kanał.

Przykład

Poniższy przykład pokazuje, jak wysyłać dane z Java NIO DataGramChannel.

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

Wynik

Server Started: localhost/127.0.0.1:8989

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

Wynik

Uruchomienie klienta spowoduje wydrukowanie następujących danych wyjściowych na serwerze.

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

Kanał gniazda Java NIO jest kanałem typu wybieralnego, co oznacza, że ​​można go multipleksować za pomocą selektora, używanego do łączenia gniazd strumieniowego przepływu danych. Kanał gniazda można utworzyć, wywołując jego open() metoda, pod warunkiem, że żadne istniejące gniazdo nie jest jeszcze obecne. Kanał gniazda jest tworzony przez wywołanie metody otwartej, ale nie jest jeszcze połączony. connect() Jednym z punktów, o którym należy tutaj wspomnieć, jest to, że jeśli kanał nie jest podłączony i próbowano wykonać jakąkolwiek operację we / wy, wówczas ten kanał generuje wyjątek NotYetConnectedException.Tak więc należy upewnić się, że kanał jest podłączony przed wykonaniem jakichkolwiek operacji we / wy. Po podłączeniu kanał pozostaje podłączony do momentu jego zamknięcia. Stan kanału gniazda można określić poprzez wywołanie jego isConnected metoda.

Połączenie kanału gniazda można zakończyć, wywołując jego finishConnect() To, czy operacja połączenia jest w toku, może być określona przez wywołanie metody isConnectionPending. Domyślnie kanał gniazda obsługuje połączenia nieblokujące. Obsługuje również zamykanie asynchroniczne, które jest podobne do asynchronicznej operacji zamykania określonej w klasie Channel.

Kanały gniazd są bezpieczne do użytku przez wiele współbieżnych wątków. Obsługują równoczesne czytanie i pisanie, chociaż co najwyżej jeden wątek może być czytany i co najwyżej jeden wątek może pisać w danym momencie. Metody connect i finishConnect są wzajemnie synchronizowane, a próba zainicjowania operacji odczytu lub zapisu, gdy trwa wywołanie jednej z tych metod, będzie blokowana do momentu zakończenia tego wywołania.

Ważne metody kanału Socket

  • bind(SocketAddress local) - Ta metoda służy do powiązania kanału gniazda z adresem lokalnym, który jest dostarczany jako parametr tej metody.

  • connect(SocketAddress remote) - Ta metoda służy do podłączenia gniazda do zdalnego adresu.

  • finishConnect() - Ta metoda służy do zakończenia procesu podłączania kanału gniazda.

  • getRemoteAddress() - Ta metoda zwraca adres zdalnej lokalizacji, do której jest podłączone gniazdo kanału.

  • isConnected() - Jak już wspomniano, ta metoda zwraca stan połączenia kanału gniazda, tj. Czy jest podłączony czy nie.

  • open() and open((SocketAddress remote) - Metoda Open służy do otwierania kanału gniazda dla żadnego określonego adresu, podczas gdy sparametryzowana metoda otwarta otwiera kanał dla określonego adresu zdalnego, a także łączy się z nim. Ta wygodna metoda działa tak, jakby wywoływała metodę open (), wywołując metodę connect na wynikowym gniazda kanału, przekazując go zdalnie, a następnie zwracając ten kanał.

  • read(ByteBuffer dst) - Ta metoda służy do odczytu danych z danego bufora przez kanał gniazda.

  • isConnectionPending() - Ta metoda określa, czy operacja połączenia jest w toku na tym kanale.

Przykład

Poniższy przykład pokazuje, jak wysyłać dane z Java NIO SocketChannel.

C: /Test/temp.txt

Hello World!

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

Wynik

Uruchomienie klienta nie spowoduje wydrukowania niczego do czasu uruchomienia serwera.

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

Wynik

Uruchomienie serwera spowoduje wydrukowanie następujących informacji.

Connection Set:  /127.0.0.1:49558
File Received

Kanał gniazda serwera Java NIO jest ponownie wybieranym typem kanału używanym do strumieniowego przepływu danych łączących gniazda. Kanał gniazda serwera można utworzyć, wywołując jego statyczny open() metoda, pod warunkiem, że żadne wcześniej istniejące gniazdo nie jest jeszcze obecne.Serwer Kanał gniazda jest tworzony przez wywołanie metody otwartej, ale nie jest jeszcze powiązany. bind() należy wywołać metodę.

Należy tu wspomnieć o tym, że jeśli kanał nie jest powiązany i próbowano wykonać jakąkolwiek operację we / wy, wówczas ten kanał generuje wyjątek NotYetBoundException, dlatego przed wykonaniem jakiejkolwiek operacji we / wy należy się upewnić, że kanał jest ograniczony.

Połączenia przychodzące dla kanału gniazda serwera są nasłuchiwane przez wywołanie metody ServerSocketChannel.accept (). Gdy metoda accept () powróci, zwraca SocketChannel z połączeniem przychodzącym. W ten sposób metoda accept () blokuje się do momentu nadejścia połączenia przychodzącego, a jeśli kanał jest w trybie nieblokującym, metoda accept natychmiast zwróci wartość null, jeśli nie ma połączeń oczekujących. W przeciwnym razie będzie blokować się na czas nieokreślony, aż dostępne będzie nowe połączenie lub wystąpi błąd we / wy.

Gniazdo nowego kanału jest początkowo niezwiązane; musi być powiązany z określonym adresem za pomocą jednej z metod wiązania jego gniazda, zanim połączenia będą mogły zostać zaakceptowane. Nowy kanał jest również tworzony przez wywołanie metody openServerSocketChannel domyślnego obiektu SelectorProvider dla całego systemu.

Podobnie jak serwer gniazda, kanał gniazda może odczytywać dane za pomocą read()Najpierw przydzielany jest bufor. Dane odczytane z ServerSocketChannel są przechowywane w buforze, po drugie wywołujemy metodę ServerSocketChannel.read () i odczytuje ona dane z ServerSocketChannel do bufora. Wartość całkowita metody read () zwraca liczbę bajtów zapisanych w buforze

Podobnie dane mogą być zapisywane w kanale gniazda serwera przy użyciu write() Powszechnie używa metody write w pętli while w celu powtórzenia metody write () do momentu, gdy Buffer nie będzie miał więcej bajtów dostępnych do zapisu.

Ważne metody kanału Socket

  • bind(SocketAddress local) - Ta metoda służy do powiązania kanału gniazda z adresem lokalnym, który jest dostarczany jako parametr tej metody.

  • accept() - Ta metoda jest używana do akceptowania połączenia z gniazdem tego kanału.

  • connect(SocketAddress remote) - Ta metoda służy do podłączenia gniazda do zdalnego adresu.

  • finishConnect() - Ta metoda służy do zakończenia procesu podłączania kanału gniazda.

  • getRemoteAddress() - Ta metoda zwraca adres zdalnej lokalizacji, do której jest podłączone gniazdo kanału.

  • isConnected() - Jak już wspomniano, ta metoda zwraca stan połączenia kanału gniazda, tj. Czy jest podłączony czy nie.

  • open() - Metoda Open służy do otwierania kanału gniazda dla żadnego określonego adresu. Ta wygodna metoda działa tak, jakby wywoływała metodę open (), wywoływała metodę connect na wynikowym kanale gniazda serwera, przekazywała go zdalnie, a następnie zwracała ten kanał.

  • read(ByteBuffer dst) - Ta metoda służy do odczytu danych z danego bufora przez kanał gniazda.

  • setOption(SocketOption<T> name, T value) - Ta metoda ustawia wartość opcji gniazda.

  • socket() - Ta metoda pobiera gniazdo serwera skojarzone z tym kanałem.

  • validOps() - Ta metoda zwraca zestaw operacji identyfikujący obsługiwane operacje tego kanału. Kanały z gniazdem serwera obsługują tylko akceptowanie nowych połączeń, więc ta metoda zwraca SelectionKey.OP_ACCEPT.

Przykład

Poniższy przykład pokazuje, jak wysyłać dane z Java NIO ServerSocketChannel.

C: /Test/temp.txt

Hello World!

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

Wynik

Uruchomienie klienta nie spowoduje wydrukowania niczego do czasu uruchomienia serwera.

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

Wynik

Uruchomienie serwera spowoduje wydrukowanie następujących informacji.

Connection Set:  /127.0.0.1:49558
File Received

Jak wiemy, Java NIO jest bardziej zoptymalizowanym interfejsem API dla operacji we / wy danych w porównaniu z konwencjonalnym interfejsem API IO języka Java. Jeszcze jednym dodatkowym wsparciem, które zapewnia Java NIO, jest odczyt / zapis danych z / do wielu buforów do kanału. i obsługa zapisu jest określana jako Scatter and Gather, w której dane są rozpraszane do wielu buforów z jednego kanału w przypadku odczytu danych, podczas gdy dane są gromadzone z wielu buforów do jednego kanału w przypadku zapisu danych.

Aby osiągnąć to wielokrotne odczytywanie i zapisywanie z kanału, istnieje API ScatteringByteChannel i GatheringByteChannel, które Java NIO zapewnia do odczytu i zapisu danych, jak pokazano w poniższym przykładzie.

ScatteringByteChannel

Read from multiple channels - W tym celu dokonaliśmy odczytu danych z pojedynczego kanału do wielu buforów.W tym celu wiele buforów jest przydzielanych i dodawanych do tablicy typu bufora, a następnie ta tablica jest przekazywana jako parametr do metody odczytu () ScatteringByteChannel, która następnie zapisuje dane z kanał w sekwencji bufory występują w tablicy. Gdy bufor się zapełni, kanał przechodzi do następnego bufora.

Poniższy przykład ilustruje sposób rozpraszania danych w 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();
      }
   }
}

Wynik

Scattering : Len1 = 1214606444
Scattering : Len2 = 0

Na koniec można stwierdzić, że metoda rozpraszania / zbierania w Java NIO jest wprowadzana jako zoptymalizowana i wielozadaniowa, gdy jest używana prawidłowo.Pozwala delegować systemowi operacyjnemu podstawową pracę polegającą na oddzieleniu danych, które czytasz do wielu wiader lub asemblacji. oddzielne fragmenty danych w jedną całość. bez wątpienia oszczędza to czas i wykorzystuje system operacyjny bardziej efektywnie, unikając kopiowania w buforze, oraz zmniejsza ilość kodu potrzebnego do napisania i debugowania.

Jak wiemy, Java NIO jest bardziej zoptymalizowanym interfejsem API dla operacji we / wy danych w porównaniu z konwencjonalnym interfejsem API IO języka Java. Jeszcze jednym dodatkowym wsparciem, które zapewnia Java NIO, jest odczyt / zapis danych z / do wielu buforów do kanału. i obsługa zapisu jest określana jako Scatter and Gather, w której dane są rozpraszane do wielu buforów z jednego kanału w przypadku odczytu danych, podczas gdy dane są gromadzone z wielu buforów do jednego kanału w przypadku zapisu danych.

Aby osiągnąć to wielokrotne odczytywanie i zapisywanie z kanału, istnieje API ScatteringByteChannel i GatheringByteChannel, które Java NIO zapewnia do odczytu i zapisu danych, jak pokazano w poniższym przykładzie.

GatheringByteChannel

write to multiple channels - W tym celu dokonaliśmy zapisu danych z wielu buforów w jednym kanale W tym celu ponownie przydzielanych jest wiele buforów i dodawanych do tablicy typu bufora, a następnie ta tablica jest przekazywana jako parametr do metody GatheringByteChannel write (), która następnie zapisuje dane z wielu buforów w sekwencji bufory występują w tablicy.Jednym punktem do zapamiętania jest to, że zapisywane są tylko dane między pozycją a granicą buforów.

Poniższy przykład przedstawia sposób zbierania danych w 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();
      }
   }
}

Wynik

Gathering : Len1 = 24
Gathering : Len2 = 25

Na koniec można stwierdzić, że metoda rozpraszania / zbierania w Java NIO jest wprowadzana jako zoptymalizowana i wielozadaniowa, gdy jest używana prawidłowo.Pozwala delegować systemowi operacyjnemu podstawową pracę polegającą na oddzieleniu danych, które czytasz do wielu wiader lub asemblacji. oddzielne fragmenty danych w jedną całość. bez wątpienia oszczędza to czas i wykorzystuje system operacyjny bardziej efektywnie, unikając kopiowania w buforze, oraz zmniejsza ilość kodu potrzebnego do napisania i debugowania.

Bufory w Java NIO mogą być traktowane jako prosty obiekt, który działa jako kontener fragmentów danych o ustalonej wielkości, który może być używany do zapisywania danych w kanale lub odczytywania danych z kanału, tak aby bufory działały jako punkty końcowe kanałów.

Zapewnia zestaw metod, które ułatwiają obsługę bloku pamięci w celu odczytu i zapisu danych do iz kanałów.

Bufory sprawiają, że pakiet NIO jest wydajniejszy i szybszy w porównaniu do klasycznych IO, ponieważ dane IO są przetwarzane w postaci strumieni, które nie obsługują asynchronicznego i współbieżnego przepływu danych, a także IO nie pozwala na wykonanie danych w kawałku lub grupie bajtów .

Podstawowe parametry definiujące bufor Java NIO można zdefiniować jako -

  • Capacity - Maksymalna ilość danych / bajtów, które mogą być przechowywane w buforze. Pojemność bufora nie może zostać zmieniona. Gdy bufor jest pełny, należy go wyczyścić przed zapisaniem do niego.

  • Limit - Limit ma znaczenie zgodnie z trybem bufora, tj. W trybie zapisu Buffer Limit jest równy pojemności, co oznacza, że ​​maksymalna ilość danych, które można zapisać w buforze, podczas gdy w trybie odczytu bufora Limit oznacza limit ilości danych, które można zapisać. czytać z bufora.

  • Position - Wskazuje bieżące położenie kursora w buforze, początkowo ustawione na 0 w momencie tworzenia bufora lub innymi słowy jest to indeks kolejnego elementu do odczytania lub zapisu, który jest aktualizowany automatycznie przez get () i put ( ) metody.

  • Mark - Zaznacz zakładkę pozycji w buforze. Kiedy wywoływana jest metoda mark (), zapisywana jest bieżąca pozycja, a wywołanie reset () przywraca zaznaczoną pozycję.

Typ bufora

Bufory Java NIO można podzielić na następujące warianty na podstawie typów danych, z którymi ma do czynienia bufor -

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

Ważne metody Buffer

Jak już wspomniano, Buffer działa jako obiekt pamięci, który dostarcza zestaw metod, które ułatwiają obsługę bloku pamięci. Poniżej przedstawiono ważne metody Buffer -

  • allocate(int capacity) - Ta metoda służy do przydzielania nowego bufora z pojemnością jako parametrem. Metoda Allocate zgłasza wyjątek IllegalArgumentException w przypadku, gdy przekazana pojemność jest ujemną liczbą całkowitą.

  • read() and put() - metoda odczytu kanału służy do zapisu danych z kanału do bufora, natomiast put jest metodą bufora, która służy do zapisywania danych w buforze.

  • flip() - Metoda odwracania przełącza tryb bufora z trybu zapisu na tryb odczytu, a także ustawia pozycję z powrotem na 0 i ustawia limit do miejsca, w którym pozycja była w momencie pisania.

  • write() and get() - metoda zapisu kanału służy do zapisywania danych z bufora do kanału, podczas gdy get jest metodą bufora, która służy do odczytu danych z bufora.

  • rewind() - metoda przewijania jest używana, gdy wymagane jest ponowne odczytanie, ponieważ ustawia pozycję z powrotem na zero i nie zmienia wartości limitu.

  • clear() and compact() - jasne i kompaktowe obie metody są używane do zmiany trybu buforowania z trybu odczytu na zapis.clear() metoda ustawia pozycję na zero, a limit jest równy pojemności, w tej metodzie dane w buforze nie są kasowane, tylko znaczniki są ponownie inicjalizowane.

    Z drugiej strony compact() metoda jest używana, gdy pozostały jakieś nieodczytane dane i nadal używamy trybu zapisu bufora w tym przypadku metoda kompaktowa kopiuje wszystkie nieprzeczytane dane na początek bufora i ustawia pozycję bezpośrednio po ostatnim nieprzeczytanym elemencie. Właściwość limit jest nadal ustawiony na pojemność.

  • mark() and reset() - Jak nazwa sugeruje, metoda mark służy do zaznaczania określonej pozycji w buforze, podczas gdy resetowanie powoduje powrót do zaznaczonej pozycji.

Przykład

Poniższy przykład przedstawia implementację wyżej zdefiniowanych metod.

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

Wynik

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

Ponieważ wiemy, że Java NIO obsługuje wiele transakcji zi do kanałów i buforów, więc w celu zbadania jednego lub więcej kanałów NIO i określenia, które kanały są gotowe do transakcji danych, tj. Odczytu lub zapisu Java NIO, należy zapewnić Selector.

Dzięki Selectorowi możemy utworzyć wątek, aby wiedzieć, który kanał jest gotowy do zapisu i odczytu danych i może obsłużyć ten konkretny kanał.

Możemy uzyskać instancję selektora, wywołując jego metodę statyczną open().Po otwarciu selektora musimy zarejestrować kanał trybu nieblokującego, który zwraca instancję SelectionKey.

SelectionKey jest w zasadzie zbiorem operacji, które można wykonać za pomocą kanału lub możemy powiedzieć, że możemy poznać stan kanału za pomocą klawisza wyboru.

Główne operacje lub stan kanału reprezentowany przez klawisz wyboru to -

  • SelectionKey.OP_CONNECT - Kanał, który jest gotowy do połączenia z serwerem.

  • SelectionKey.OP_ACCEPT - Kanał, który jest gotowy do przyjmowania połączeń przychodzących.

  • SelectionKey.OP_READ - Kanał gotowy do odczytu danych.

  • SelectionKey.OP_WRITE - Kanał gotowy do zapisu danych.

Klucz wyboru uzyskany po rejestracji ma kilka ważnych metod, jak wspomniano poniżej -

  • attach() - Ta metoda służy do dołączania obiektu za pomocą klucza, a głównym celem dołączenia obiektu do kanału jest rozpoznanie tego samego kanału.

  • attachment() - Ta metoda służy do zatrzymania dołączonego obiektu z kanału.

  • channel() - Ta metoda służy do pobrania kanału, dla którego utworzono określony klucz.

  • selector() - Ta metoda służy do pobrania selektora, dla którego utworzono określony klucz.

  • isValid() - Ta metoda zwraca, czy klucz jest ważny, czy nie.

  • isReadable() - Ta metoda stwierdza, że ​​kanał klucza pogody jest gotowy do odczytu lub nie.

  • isWritable() - Ta metoda stwierdza, że ​​kanał klucza pogody jest gotowy do zapisu, czy nie.

  • isAcceptable() - Ta metoda stwierdza, że ​​kanał klucza pogodowego jest gotowy do przyjęcia połączenia przychodzącego lub nie.

  • isConnectable() - Ta metoda sprawdza, czy kanał tego klucza zakończył lub nie zakończył operacji połączenia z gniazdem.

  • isAcceptable() - Ta metoda sprawdza, czy kanał tego klucza jest gotowy do przyjęcia nowego połączenia przez gniazdo.

  • interestOps() - Ta metoda pobiera zestaw odsetek dla tego klucza.

  • readyOps() - Ta metoda pobiera gotowy zestaw, czyli zestaw operacji, do których kanał jest gotowy.

Możemy wybrać kanał z selektora, wywołując jego statyczną metodę select().Select metoda selektora jest przeciążona jako -

  • select() - Ta metoda blokuje bieżący wątek, dopóki co najmniej jeden kanał nie będzie gotowy na zdarzenia, dla których jest zarejestrowany.

  • select(long timeout) - Ta metoda działa tak samo jak select (), z tą różnicą, że blokuje wątek na maksymalny limit czasu w milisekundach (parametr).

  • selectNow() - Ta metoda nie blokuje się w ogóle, wraca natychmiast z wszystkimi kanałami, które są gotowe.

Również w celu pozostawienia zablokowanego wątku, który wywołuje metodę select,wakeup() metodę można wywołać z instancji selektora, po czym wątek oczekujący wewnątrz funkcji select () powróci natychmiast.

W końcu możemy zamknąć selektor, wywołując close() , która również unieważnia wszystkie wystąpienia SelectionKey zarejestrowane za pomocą tego Selectora wraz z zamknięciem selektora.

Przykład

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

W Javie NIO potok jest komponentem służącym do zapisu i odczytu danych pomiędzy dwoma wątkami. Rura składa się głównie z dwóch kanałów odpowiedzialnych za propagację danych.

Spośród dwóch składowych kanałów jeden nazywany jest kanałem Sink, który służy głównie do zapisywania danych, a drugi jest kanałem źródłowym, którego głównym celem jest odczyt danych z kanału Sink.

Synchronizacja danych jest utrzymywana w porządku podczas zapisywania i odczytu danych, ponieważ należy zapewnić, aby dane były odczytywane w tej samej kolejności, w jakiej są zapisywane w potoku.

Należy pamiętać, że jest to jednokierunkowy przepływ danych w potoku, tj. Dane są zapisywane tylko w kanale Sink i mogą być odczytywane tylko z kanału źródłowego.

W Javie potok NIO jest definiowany jako klasa abstrakcyjna, składająca się głównie z trzech metod, z których dwie są abstrakcyjne.

Metody klasy Pipe

  • open() - Ta metoda jest używana do uzyskania instancji Pipe lub możemy powiedzieć, że potok jest tworzony przez wywołanie tej metody.

  • sink() - Ta metoda zwraca kanał ujścia potoku, który jest używany do zapisywania danych, wywołując jego metodę zapisu.

  • source() - Ta metoda zwraca kanał źródłowy potoku, który jest używany do odczytu danych, wywołując jego metodę odczytu.

Przykład

Poniższy przykład przedstawia implementację potoku Java NIO.

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

Wynik

Test Data to Check java NIO Channels Pipe.

Zakładając, że mamy plik tekstowy c:/test.txt, który ma następującą treść. Ten plik zostanie użyty jako dane wejściowe dla naszego przykładowego programu.

Jak sama nazwa wskazuje, Ścieżka to konkretna lokalizacja jednostki, takiej jak plik lub katalog w systemie plików, dzięki czemu można wyszukiwać i uzyskiwać do nich dostęp w tej konkretnej lokalizacji.

Z technicznego punktu widzenia, ścieżka jest interfejsem, który jest wprowadzany w pakiecie plików Java NIO w wersji Java 7 i jest reprezentacją lokalizacji w danym systemie plików.Ponieważ interfejs ścieżki znajduje się w pakiecie Java NIO, otrzymuje kwalifikowaną nazwę java .nio.file.Path.

Ogólnie ścieżka jednostki może być dwojakiego rodzaju, jedna to ścieżka bezwzględna, a druga to ścieżka względna.Jak nazwa obu ścieżek sugeruje, że ścieżka bezwzględna jest adresem lokalizacji od źródła do jednostki, w której się znajduje, podczas gdy ścieżka względna jest adresem lokalizacji która jest względna w stosunku do innej ścieżki. Ścieżka używa separatorów w swojej definicji jako „\” dla Windows i „/” dla systemów operacyjnych unix.

W celu pobrania instancji Path możemy skorzystać ze statycznej metody klasy java.nio.file.Paths get()Ta metoda konwertuje ciąg ścieżki lub sekwencję ciągów, które po połączeniu tworzą ciąg ścieżki, na instancję Path.Ta metoda również generuje wyjątek InvalidPathException środowiska wykonawczego, jeśli przekazane argumenty zawierają niedozwolone znaki.

Jak wspomniano powyżej, ścieżka bezwzględna jest pobierana przez przekazanie elementu głównego i pełnej listy katalogów wymaganych do zlokalizowania pliku, podczas gdy ścieżkę względną można pobrać, łącząc ścieżkę podstawową ze ścieżką względną. Pobieranie obu ścieżek zostanie zilustrowane w poniższym przykładzie

Przykład

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

Jak dotąd wiemy, co to jest interfejs ścieżki, dlaczego tego potrzebujemy i jak możemy uzyskać do niego dostęp. Teraz wiedzielibyśmy, jakie są ważne metody, które zapewnia nam interfejs Path.

Ważne metody interfejsu ścieżki

  • getFileName() - Zwraca system plików, który utworzył ten obiekt.

  • getName() - Zwraca element nazwy tej ścieżki jako obiekt Path.

  • getNameCount() - Zwraca liczbę elementów nazwy w ścieżce.

  • subpath() - Zwraca ścieżkę względną, która jest podciągiem elementów nazwy tej ścieżki.

  • getParent() - Zwraca ścieżkę nadrzędną lub null, jeśli ta ścieżka nie ma rodzica.

  • getRoot() - Zwraca główny składnik tej ścieżki jako obiekt Path lub wartość null, jeśli ta ścieżka nie ma składnika głównego.

  • toAbsolutePath() - Zwraca obiekt Path reprezentujący bezwzględną ścieżkę tej ścieżki.

  • toRealPath() - Zwraca rzeczywistą ścieżkę do istniejącego pliku.

  • toFile() - Zwraca obiekt File reprezentujący tę ścieżkę.

  • normalize() - Zwraca ścieżkę, która jest tą ścieżką z wyeliminowanymi zbędnymi elementami nazwy.

  • compareTo(Path other) - Porównuje dwie abstrakcyjne ścieżki leksykograficznie Ta metoda zwraca zero, jeśli argument jest równy tej ścieżce, wartość mniejszą niż zero, jeśli ta ścieżka jest leksykograficznie mniejsza niż argument, lub wartość większą niż zero, jeśli ta ścieżka jest leksykograficznie większa niż argument .

  • endsWith(Path other) - Testuje, czy ścieżka kończy się podaną ścieżką Jeśli dana ścieżka ma N elementów i nie ma komponentu głównego, a ta ścieżka ma N lub więcej elementów, to ścieżka kończy się podaną ścieżką, jeśli ostatnie N elementów każdej ścieżki, zaczynając od elementu najbardziej oddalonego od korzenia, są równe.

  • endsWith(String other) - Testuje, czy ta ścieżka kończy się ścieżką, skonstruowaną przez konwersję podanego ciągu ścieżki, dokładnie w sposób określony przez metodę endWith (Path).

Przykład

Poniższy przykład ilustruje różne metody interfejsu Path, które zostały wymienione powyżej -

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

Pakiet Java NIO zapewnia jeszcze jedno narzędzie API o nazwie Pliki, które jest zasadniczo używane do manipulowania plikami i katalogami przy użyciu statycznych metod, które działają głównie na obiekcie Path.

Jak wspomniano w samouczku dotyczącym ścieżki, interfejs ścieżki jest wprowadzany w pakiecie Java NIO w wersji Java 7 w pakiecie plików, więc ten samouczek dotyczy tego samego pakietu plików.

Ta klasa składa się wyłącznie z metod statycznych, które działają na plikach, katalogach lub innych typach plików. W większości przypadków metody zdefiniowane w tym miejscu będą delegować do powiązanego dostawcy systemu plików wykonywanie operacji na plikach.

Istnieje wiele metod zdefiniowanych w klasie Files, które można również odczytać z dokumentacji Java. W tym samouczku staraliśmy się omówić niektóre z ważnych metod spośród wszystkich metod klasy Java NIO Files.

Ważne metody klasy Files.

Poniżej przedstawiono ważne metody zdefiniowane w klasie Java NIO Files.

  • createFile(Path filePath, FileAttribute attrs) - Klasa Files zapewnia tę metodę tworzenia pliku przy użyciu określonej ścieżki.

Przykład

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

Wynik

Created a file at : D:\data\file.txt
  • copy(InputStream in, Path target, CopyOption… options) - Ta metoda służy do kopiowania wszystkich bajtów z określonego strumienia wejściowego do określonego pliku docelowego i zwraca liczbę bajtów odczytanych lub zapisanych jako długa wartość. LinkOption dla tego parametru z następującymi wartościami -

    • COPY_ATTRIBUTES - skopiuj atrybuty do nowego pliku, np. Atrybut czasu ostatniej modyfikacji.

    • REPLACE_EXISTING - zamień istniejący plik, jeśli istnieje.

    • NOFOLLOW_LINKS - Jeśli plik jest dowiązaniem symbolicznym, to kopiowane jest samo łącze, a nie cel łącza.

Przykład

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

Wynik

To be or not to be?
  • createDirectories(Path dir, FileAttribute<?>...attrs) - Ta metoda jest używana do tworzenia katalogów przy użyciu podanej ścieżki poprzez tworzenie wszystkich nieistniejących katalogów nadrzędnych.

  • delete(Path path) - Ta metoda służy do usuwania pliku z określonej ścieżki. Wyrzuca NoSuchFileException, jeśli plik nie istnieje w określonej ścieżce lub jeśli plik jest katalogiem i nie może być pusty i nie można go usunąć.

  • exists(Path path) - Ta metoda służy do sprawdzenia, czy plik istnieje w określonej ścieżce i jeśli plik istnieje, zwróci wartość true, w przeciwnym razie zwróci false.

  • readAllBytes(Path path) - Ta metoda jest używana do odczytu wszystkich bajtów z pliku pod podaną ścieżką i zwraca tablicę bajtów zawierającą bajty odczytane z pliku.

Przykład

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

Wynik

Welcome to file.
  • size(Path path) - Ta metoda służy do pobierania rozmiaru pliku w określonej ścieżce w bajtach.

  • write(Path path, byte[] bytes, OpenOption… options) - Ta metoda służy do zapisywania bajtów do pliku w określonej ścieżce.

Przykład

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

Wynik

To be or not to be?

Ponieważ wiemy, że Java NIO obsługuje współbieżność i wielowątkowość, co pozwala nam obsługiwać różne kanały jednocześnie w tym samym czasie, więc API, które jest za to odpowiedzialne w pakiecie Java NIO, to AsynchronousFileChannel, który jest zdefiniowany w pakiecie kanałów NIO. dla AsynchronousFileChannel to java.nio.channels.AsynchronousFileChannel.

AsynchronousFileChannel jest podobny do tego z FileChannel NIO, z tą różnicą, że ten kanał umożliwia wykonywanie operacji na plikach w sposób asynchroniczny, w przeciwieństwie do synchronicznej operacji we / wy, w której wątek wchodzi do akcji i czeka, aż żądanie zostanie zakończone. Dlatego kanały asynchroniczne są bezpieczne w użyciu przez wiele współbieżnych wątków.

W trybie asynchronicznym żądanie jest przesyłane wątkiem do jądra systemu operacyjnego, aby to zrobić, podczas gdy wątek kontynuuje przetwarzanie innego zadania. Po zakończeniu zadania jądra sygnalizuje wątek, a następnie wątek potwierdza sygnał i przerywa bieżące zadanie i przetwarza Zadanie we / wy w razie potrzeby.

Aby osiągnąć współbieżność, ten kanał zapewnia dwa podejścia, w tym jeden jako zwracający plik java.util.concurrent.Future object a inny przekazuje do operacji obiekt typu java.nio.channels.CompletionHandler.

Zrozumiemy oba podejścia za pomocą przykładów jeden po drugim.

  • Future Object - W tym przypadku z kanału zwracana jest instancja Future Interface, w przypadku interfejsu Future jest get() metoda zwracająca status operacji obsługiwanej asynchronicznie, na podstawie której można by zdecydować o dalszym wykonaniu innego zadania, a także sprawdzić, czy zadanie zostało zakończone, wywołując jego isDone metoda.

Przykład

Poniższy przykład pokazuje, jak używać obiektu Future i wykonywać zadania asynchronicznie.

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

Wynik

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 -

    To podejście jest dość proste, ponieważ w tym przypadku używamy interfejsu CompletionHandler i przesłaniamy jego dwie metody, z których jedna jest completed() metoda, która jest wywoływana, gdy operacja we / wy zakończy się pomyślnie, a inne są failed() metoda wywoływana w przypadku niepowodzenia operacji we / wy. w tym przypadku tworzona jest procedura obsługi w celu wykorzystania wyniku asynchronicznej operacji we / wy, ponieważ po zakończeniu zadania tylko procedura obsługi ma funkcje, które są wykonywane.

Przykład

Poniższy przykład pokazuje, jak używać CompletionHandler do wykonywania zadań asynchronicznie.

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

Wynik

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.

W Javie dla każdego znaku istnieją dobrze zdefiniowane jednostki kodu Unicode, które są wewnętrznie obsługiwane przez JVM, więc pakiet Java NIO definiuje klasę abstrakcyjną o nazwie Charset, która jest używana głównie do kodowania i dekodowania zestawu znaków i UNICODE.

Standardowe zestawy znaków

Obsługiwane zestawy znaków w java podano poniżej.

  • US-ASCII - Siedem bitowych znaków ASCII.

  • ISO-8859-1 - Alfabet łaciński ISO.

  • UTF-8 - To jest 8-bitowy format transformacji UCS.

  • UTF-16BE - To jest 16-bitowy format transformacji UCS z kolejnością bajtów big endian.

  • UTF-16LE - To jest 16-bitowa transformacja UCS z małą kolejnością bajtów endian.

  • UTF-16 - 16-bitowy format transformacji UCS.

Ważne metody klasy Charset

  • forName() - Ta metoda tworzy obiekt zestawu znaków dla podanej nazwy zestawu znaków.Nazwa może być kanoniczna lub alias.

  • displayName() - Ta metoda zwraca kanoniczną nazwę podanego zestawu znaków.

  • canEncode() - Ta metoda sprawdza, czy dany zestaw znaków obsługuje kodowanie, czy nie.

  • decode() - Ta metoda dekoduje ciąg podanego zestawu znaków do bufora znaków zestawu Unicode.

  • encode() - Ta metoda koduje bufor znaków Unicode w buforze bajtów danego zestawu znaków.

Przykład

Poniższy przykład ilustruje ważne metody klasy 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();
   }
}

Wynik

US-ASCII
Demo text for conversion.

Ponieważ wiemy, że Java NIO obsługuje współbieżność i wielowątkowość, co pozwala mu radzić sobie z wieloma wątkami działającymi na wielu plikach w tym samym czasie, ale w niektórych przypadkach wymagamy, aby nasz plik nie był udostępniany przez żaden z wątków i był niedostępny.

W przypadku takich wymagań NIO ponownie udostępnia API znane jako FileLock, które jest używane do zapewnienia blokady całego pliku lub części pliku, tak aby ten plik lub jego część nie był udostępniany ani dostępny.

aby zapewnić lub zastosować taką blokadę musimy skorzystać z FileChannel lub AsynchronousFileChannel, który udostępnia dwie metody lock() i tryLock()w tym celu. dostępny zamek może być dwojakiego rodzaju -

  • Exclusive Lock - Blokada na wyłączność zapobiega uzyskaniu przez inne programy nakładającej się blokady dowolnego typu.

  • Shared Lock - Blokada współdzielona uniemożliwia innym współbieżnie działającym programom uzyskanie nakładającej się blokady na wyłączność, ale umożliwia im uzyskanie nakładających się blokad współdzielonych.

Metody używane do uzyskania blokady nad plikiem -

  • lock() - Ta metoda FileChannel lub AsynchronousFileChannel uzyskuje wyłączną blokadę na plik skojarzony z danym kanałem. Typ zwrotny tej metody to FileLock, który jest dalej używany do monitorowania uzyskanej blokady.

  • lock(long position, long size, boolean shared) - Ta metoda jest ponownie przeciążoną metodą blokady i służy do blokowania określonej części pliku.

  • tryLock() - Ta metoda zwraca FileLock lub null, jeśli nie można uzyskać blokady i próbuje uzyskać jawnie wyłączną blokadę w pliku tego kanału.

  • tryLock(long position, long size, boolean shared) - Ta metoda próbuje uzyskać blokadę w danym regionie pliku tego kanału, który może być typu wyłącznego lub współdzielonego.

Metody klasy FileLock

  • acquiredBy() - Ta metoda zwraca kanał, na którym uzyskano blokadę pliku.

  • position() - Ta metoda zwraca pozycję w pliku pierwszego bajtu zablokowanego regionu. Zablokowany region nie musi znajdować się w rzeczywistym pliku bazowym, ani nawet go nakładać, więc wartość zwracana przez tę metodę może przekraczać aktualny rozmiar pliku.

  • size() - Ta metoda zwraca rozmiar zablokowanego regionu w bajtach. Zablokowany region nie musi znajdować się w rzeczywistym pliku bazowym, ani nawet go nakładać, więc wartość zwracana przez tę metodę może przekraczać bieżący rozmiar pliku.

  • isShared() - Ta metoda służy do określenia, czy blokada jest współdzielona, ​​czy nie.

  • overlaps(long position,long size) - Ta metoda określa, czy ta blokada nakłada się na dany zakres blokady.

  • isValid() - Ta metoda określa, czy uzyskana blokada jest prawidłowa. Obiekt blokady pozostaje ważny, dopóki nie zostanie zwolniony lub powiązany kanał pliku zostanie zamknięty, w zależności od tego, co nastąpi wcześniej.

  • release()- Zwalnia uzyskaną blokadę.Jeśli obiekt blokady jest prawidłowy, wywołanie tej metody zwalnia blokadę i powoduje, że obiekt jest nieprawidłowy. Jeśli ten obiekt blokady jest nieprawidłowy, wywołanie tej metody nie ma żadnego efektu.

  • close()- Ta metoda wywołuje metodę release (). Został dodany do klasy, aby można go było używać w połączeniu z konstrukcją bloku automatycznego zarządzania zasobami.

Przykład pokazujący blokadę plików.

Poniższy przykład utwórz blokadę pliku i zapisz do niego zawartość

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

Wynik

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.