Advanced Java Services | 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.
Hier wird zunächst der AsynchronousFileChannel behandelt.
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 Returntype | Method 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 extends OpenOption> 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 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 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.
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()
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.
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 }