Advanced   Java   Services Die nio.channels Hierarchie Back Next Up Home


Die nio.channels Hierarchie

Die in 1.4 eingeführten Channesl wurden in 1.7 um die asynchronen Channels und um den SeekableByteChannel ergänzt. NetworkChannel und MulticastChannel sind dagegen interne Ergänzungen der Hierarchie.

java-channels-hierarchie.jpg

Hier wird zunächst der AsynchronousFileChannel behandelt.


AsynchronousFileChannel

Der Zugriff auf die Daten in den NIO Channels erfolgt über Buffers. Die Channels schreiben gelesene Daten in einen Buffer und umgekehrt lesen Sie den Inhalt von Buffers aus um Daten zu schreiben. Es gibt zwei statische open-Methoden, die einen AsynchronousFileChannel liefern, und jeweils zwei Lese- und Schreibmethoden.

Modifier and ReturntypeMethod and Description
static AsynchronousFileChannel open(Path file, OpenOption... options) 
Opens or creates a file for reading and/or writing, returning an asynchronous file channel to access the file.
static AsynchronousFileChannel open(Path file, Set options, ExecutorService executor, FileAttribute... attrs)
Opens or creates a file for reading and/or writing, returning an asynchronous file channel to access the file.
abstract Future<Integer> read(ByteBuffer dst, long position)
Reads a sequence of bytes from this channel into the given buffer, starting at the given file position.
abstract <A>void read(ByteBuffer dst, long position, A attachment, CompletionHandler handler)
Reads a sequence of bytes from this channel into the given buffer, starting at the given file position.
abstract Future<Integer> write(ByteBuffer src, long position)
Writes a sequence of bytes to this channel from the given buffer, starting at the given file position.
abstract <A>void write(ByteBuffer src, long position, A attachment, CompletionHandler handler)
Writes a sequence of bytes to this channel from the given buffer, starting at the given file position.

Wie der Name schon erkennen läßt werden die Daten asynchron gelesen, was heißt, daß die read- und write-Methoden nicht blockieren. Statt zu warten kann also ein Programm andere Aufgaben erledigen bis die Daten gelesen oder geschrieben sind. Trotzdem will man wissen, wann die Vorgänge beendet sind. Dazu gibt es zwei Möglichkeiten. Entweder man wertet das Futureobjekt aus, das die read- bzw. write-Methoden zurückgeben oder man implementiert das Interface CompletionHandler, das genauere Information über den Lese- oder Schreibvorgang liefert.


Beispiel 1

Das Beispiel verwendet die einfachste read()-Methode. read() blockiert nicht und liefert ein Futureobjekt. Mit diesem Objekt kann man die gelesenen daten abholen. Die get()-Methode von Future blockiert dann und liefert die Anzahl der gelesenen Bytes. Die Daten werden in den anfangs angelegten ByteBuffer geschrieben. Da eine kleine Datei gelesen wird, kann der Buffer einfach die Größe der Datei haben.

public static void beispiel1()
{
   //The file to read the contents from.
   Path path = Paths.get("einstein.txt");

   //Create the asynchronous channel in try with resources
   try(AsynchronousFileChannel asyncChannel = AsynchronousFileChannel.open(path);)
   {
      // Buffer in der Größe der Datei bereitstellen.
      int size = (int) Files.size(path);
      ByteBuffer buffer = ByteBuffer.allocate(size);

      Future future = asyncChannel.read(buffer, 0); // blockiert nicht

      while(!future.isDone())
      {
         // do some other stuff
      }

      // number of bytes read.
      System.out.println("number of bytes read: " + future.get()); // blockiert

      buffer.rewind();

      //Daten auf Konsole ausgeben
      System.out.println(new String(buffer.array());  //  use the platform's default charset.
   }
   catch(IOException | InterruptedException | ExecutionException ex)
   {
      ex.printStackTrace();
   }
}

Da die Bytes im Buffer einen Text darstellen müssen sie in einen String verwandelt werden. Dafür gibt es verschiedene Möglichkeiten:

String st = new String(buffer.array());   //  use the platform's default charset.

oder

String st = new String(buffer.array(), Charset.defaultCharset());

oder

CharBuffer charBuffer = Charset.defaultCharset().decode(buffer);
String content = charBuffer.toString()

CompletionHandler

Das Interface CompletionHandler läßt zwei generische Parameter zu und vereinbart die Methoden void completed(V result, A attachment) und void failed(Throwable exc, A attachment). Im nächsten Beispiel wird eine Datei in mehreren Teilen in einen ByteBuffer eingelesen. Das Einlesen findet im CompletionHandler statt.


Beispiel 2
public class AsynchronousFileChannelReadDemo2
{
   public static void main(String[] args)
   {
      //The file to read the contents from.
      Path path = Paths.get("einstein.txt");
      ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

      try(AsynchronousFileChannel asyncChannel = AsynchronousFileChannel.open(path,
            EnumSet.of(StandardOpenOption.READ), cachedThreadPool))
      {
         ByteBuffer buffer = ByteBuffer.allocate(256);
         MyCompletionHandler completionHandler = new MyCompletionHandler(asyncChannel, buffer);
         // Alle Lesevorgänge werden vom CompletionHandler getriggert

         completionHandler.awaitTermination();
         //executor.awaitTermination(2, TimeUnit.SECONDS);
         byte[] data = completionHandler.getBytes();
         System.out.println(new String(data).trim());
         cachedThreadPool.shutdown();
      }
      catch(IOException | InterruptedException /*| ExecutionException*/ ex)
      {
         ex.printStackTrace();
      }
   } // end main


   /*
      CompletionHandler<Integer,? super A>
      Der ByteBuffer muß von außen kommen
      Ein Allokieren im Konstruktor ist zu spät
   */
   static class MyCompletionHandler implements CompletionHandler
   {
      ByteBuffer buffer;
      AsynchronousFileChannel asyncChannel;
      int startpos = 0;
      boolean completed = false;
      List<byte[]>list = new ArrayList<>();

      public MyCompletionHandler(AsynchronousFileChannel asyncChannel, ByteBuffer buffer)
      {
         this.buffer = buffer;
         this.asyncChannel = asyncChannel;
         // erstes lesen
         asyncChannel.read(buffer, 0, null, this); // Attachment null
      }

      @Override
      public void completed(Integer result, Void v)
      {
         if (result > 0)
         {
            // Man darf nicht den originalen Buffer adden, sondern muß eine Kopie machen
            byte[] tmp = new byte[result];
            System.arraycopy(buffer.array(), 0, tmp, 0, result);
            list.add(tmp);
            // alten ByteBufferinhalt löschen
            Arrays.fill(buffer.array(), (byte)0);
            buffer.rewind();
            startpos += result;
            asyncChannel.read(buffer, startpos, null, this);
         }
         else // result < 0 also -1
         {
            completed = true;
            System.out.println("EOF reached");
         }
      }

      @Override
      public void failed(Throwable ex, Void v)
      {
         System.out.println("failed " + ex);
         completed = true;
      }

      public void awaitTermination() throws InterruptedException
      {
         for(;!completed;)
         {
            Thread.sleep(5);
         }
      }

      public byte[] getBytes()
      {
         byte[] combined = new byte[list.size() * buffer.array().length];
         int destPos = 0;
         for(int i=0; i < list.size(); i++)
         {
            byte[] tmp = list.get(i);
            System.arraycopy(tmp, 0, combined, destPos, tmp.length);
            destPos += tmp.length;
         }
         return combined;
      }

   } // end MyCompletionHandler
}



Valid XHTML 1.0 Strict top Back Next Up Home