{"id":9864,"date":"2020-02-26T09:00:00","date_gmt":"2020-02-26T08:00:00","guid":{"rendered":"https:\/\/www.happycoders.eu\/?p=9864"},"modified":"2024-11-29T16:33:47","modified_gmt":"2024-11-29T15:33:47","slug":"filechannel-memory-mapped-io-locks","status":"publish","type":"post","link":"https:\/\/www.happycoders.eu\/de\/java\/filechannel-memory-mapped-io-locks\/","title":{"rendered":"FileChannel, Memory-Mapped I\/O, Locks (Java Datei Tutorial)"},"content":{"rendered":"\n<p>In den bisherigen f\u00fcnf Teilen dieser Artikelserie ging es um das <a href=\"\/de\/java\/dateien-einfach-schnell-lesen\/\">Lesen<\/a> und <a href=\"\/de\/java\/dateien-schnell-einfach-schreiben\/\">Schreiben von Dateien<\/a>, die <a href=\"\/de\/java\/dateinamen-verzeichnisnamen-file-path-paths\/\">Konstruktion von Verzeichnis- und Dateipfaden<\/a>, <a href=\"\/de\/java\/verzeichnisse-listen-dateien-verschieben-kopieren-loeschen\/\">Verzeichnis- und Dateioperationen<\/a> sowie das <a href=\"\/de\/java\/strukturierte-daten-schreiben-lesen-dataoutputstream-datainputstream\/\">Schreiben und Lesen strukturierter Daten<\/a>.<\/p>\n\n\n\n<p>Im heutigen Teil erkl\u00e4re ich die in Java 1.4 mit dem <a rel=\"noopener\" href=\"https:\/\/jcp.org\/en\/jsr\/detail?id=51\" target=\"_blank\">JSR 51 (\"New I\/O APIs for the JavaTM Platform\")<\/a> eingef\u00fchrten NIO-<code>FileChannel<\/code> und <code>ByteBuffer<\/code> und zeige, welche M\u00f6glichkeiten es gibt damit Dateien zu lesen und zu schreiben, und was die Vorteile gegen\u00fcber den bisher vorgestellten Methoden sind.<\/p>\n\n\n\n<p>Im einzelnen lernst Du:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Was sind <code>FileChannel<\/code> und <code>ByteBuffer<\/code> und was sind deren Vorteile?<\/li>\n\n\n\n<li>Wie schreibt und liest man Dateien mit <code>FileChannel<\/code> und <code>ByteBuffer<\/code>?<\/li>\n\n\n\n<li>Was sind memory-mapped Files und deren Vorteile?<\/li>\n\n\n\n<li>Wie k\u00f6nnen einzelne Bereiche einer Datei gesperrt werden?<\/li>\n\n\n\n<li>Welche Schreibmethode hat die beste Performance?<\/li>\n<\/ul>\n\n\n\n<p>Den Code aus diesem Artikel findest du in meinem <a href=\"https:\/\/github.com\/SvenWoltmann\/filechannel-bytebuffer-memory-mapped-io-locks\" target=\"_blank\" rel=\"noopener\">GitHub-Repository<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"begriffserklaerungen\">Begriffserkl\u00e4rungen<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"was-ist-ein-filechannel\">Was ist ein FileChannel?<\/h3>\n\n\n\n<p>Ein <code>Channel<\/code> ist eine Kommunikationsverbindung zu einer Datei, zu einem Socket, oder einer anderen Komponente, die I\/O-Funktionalit\u00e4t zur Verf\u00fcgung stellt. Im Gegensatz zu einem <code>InputStream<\/code> oder <code>OutputStream<\/code> ist der <code>Channel<\/code> bidirektional, das hei\u00dft er kann zum Schreiben <em>und<\/em> Lesen verwendet werden. <\/p>\n\n\n\n<p>Ein <code>FileChannel<\/code> ist ein spezieller <code>Channel<\/code> f\u00fcr die Verbindung zu einer Datei.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"was-ist-ein-bytebuffer\">Was ist ein ByteBuffer?<\/h3>\n\n\n\n<p>Ein <code>ByteBuffer<\/code> ist im Grunde ein Byte-Array (auf dem Heap oder im nativen Speicher), kombiniert mit Schreib- und Lesefunktionen. So kann in den <code>ByteBuffer<\/code> geschrieben bzw. daraus gelesen werden, ohne die Position der geschriebenen \/ gelesenen Daten innerhalb des eigentlichen Arrays kennen zu m\u00fcssen.<\/p>\n\n\n\n<p>Wie ein ByteBuffer genau funktioniert, erf\u00e4hrst du im <a href=\"\/de\/java\/bytebuffer-flip-compact\/\">Hauptartikel \u00fcber ByteBuffer<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"dateizugriff-mit-filechannel-bytebuffer\">Dateizugriff mit FileChannel + ByteBuffer<\/h3>\n\n\n\n<p>Um Daten in einen <code>FileChannel<\/code> zu schreiben oder sie daraus zu lesen, ben\u00f6tigt man einen <code>ByteBuffer<\/code>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img decoding=\"async\" width=\"707\" height=\"94\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/ByteBuffer_FileChannel_File.png\" alt=\"Zugriff auf eine Datei \u00fcber FileChannel und ByteBuffer\" class=\"wp-image-10150\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/ByteBuffer_FileChannel_File.png 707w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/ByteBuffer_FileChannel_File-224x30.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/ByteBuffer_FileChannel_File-336x45.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/ByteBuffer_FileChannel_File-504x67.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/ByteBuffer_FileChannel_File-672x89.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/ByteBuffer_FileChannel_File-400x53.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/ByteBuffer_FileChannel_File-600x80.png 600w\" sizes=\"(max-width: 707px) 100vw, 707px\" \/><figcaption class=\"wp-element-caption\">Zugriff auf eine Datei \u00fcber FileChannel und ByteBuffer<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Daten werden mit <code>put()<\/code> in den <code>ByteBuffer<\/code> gelegt und dann mit <code>FileChannel.write(buffer)<\/code> vom Buffer in die Datei geschrieben. <code>FileChannel.write()<\/code> ruft dabei auf dem Buffer <code>get()<\/code> auf, um die Daten zu entnehmen.<\/p>\n\n\n\n<p>Mit <code>FileChannel.read(buffer)<\/code> werden Daten aus der Datei gelesen. Die <code>read()<\/code>-Methode legt die Daten mit <code>put()<\/code> in den <code>ByteBuffer<\/code>, woraus du sie dann mit <code>get()<\/code> wieder entnehmen kannst.<\/p>\n\n\n<div class=\"convertkit-form wp-block-convertkit-form\" style=\"\"><script async data-uid=\"1427197203\" src=\"https:\/\/happycoders.kit.com\/1427197203\/index.js\" data-jetpack-boost=\"ignore\" data-no-defer=\"1\" data-no-optimize=\"1\" nowprocket><\/script><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"vorteile-von-filechannel\">Vorteile von FileChannel<\/h2>\n\n\n\n<p>Der <code>FileChannel<\/code> bietet gegen\u00fcber den in den ersten zwei Teilen der Serie vorgestellten Klassen <code><a href=\"\/de\/java\/dateien-einfach-schnell-lesen\/#Lesen_grosser_Binaerdateien_mit_FileInputStream\">FileInputStream<\/a><\/code> und <code><a href=\"\/de\/java\/dateien-schnell-einfach-schreiben\/#Schreiben_einzelner_Bytes_mit_dem_FileOutputStream\">FileOutputStream<\/a><\/code> die folgenden Vorteile:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Du kannst an <strong>beliebiger Position<\/strong> innerhalb der Datei schreiben und lesen.<\/li>\n\n\n\n<li>Du kannst das Betriebssystem veranlassen ge\u00e4nderte Daten vom Cache auf das Speichermedium zu schreiben (\u201e<strong>force<\/strong>\u201c).<\/li>\n\n\n\n<li>Bereiche der Datei k\u00f6nnen in den Speicher gemappt werden (\u201e<strong>memory-mapped file<\/strong>\u201c), was besonders effizienten Datenzugriff erlaubt.<\/li>\n\n\n\n<li>Bereiche der Datei k\u00f6nnen f\u00fcr andere Threads und Prozesse gesperrt werden (\u201e<strong>file lock<\/strong>\u201c).<\/li>\n\n\n\n<li>Daten k\u00f6nnen besonders effizient von einem Channel zum anderen \u00fcbertragen werden.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"dateien-lesen-und-schreiben-mit-filechannel-und-bytebuffer\">Dateien lesen und schreiben mit FileChannel und ByteBuffer<\/h2>\n\n\n\n<p>In diesem Kapitel zeige ich dir anhand von Code-Beispielen, wie du mit <code>FileChannel<\/code> und <code>ByteBuffer<\/code> Daten schreiben und lesen kannst, wie du auf bestimmte Positionen innerhalb der Datei zugreifst, wie du die Dateigr\u00f6\u00dfe ermittelst und \u00e4nderst, und wie Du das Schreiben vom Cache aufs Speichermedium veranlasst.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"wie-liest-man-eine-datei-mit-filechannel\">Wie liest man eine Datei mit FileChannel?<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">\u00d6ffnen eines FileChannels zum Lesen einer Datei<\/h4>\n\n\n\n<p>Um eine Datei zu lesen, muss zun\u00e4chst ein <code>FileChannel<\/code> ge\u00f6ffnet werden. Die direkteste Variante daf\u00fcr ist:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\">Path path = ...;\nFileChannel channel = FileChannel.open(path, StandardOpenOption.READ);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>(<a href=\"\/de\/java\/dateinamen-verzeichnisnamen-file-path-paths\/\">Wie Du ein <code>Path<\/code>-Objekt konstruierst, kannst du im dritten Teil dieser Serie nachlesen.<\/a>)<\/p>\n\n\n\n<p>Alternativ kannst Du einen <code>FileChannel<\/code> zum Lesen auch \u00fcber <code>RandomAccessFile<\/code> erzeugen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\">Path path = ...;\nRandomAccessFile file = <span class=\"hljs-keyword\">new<\/span> RandomAccessFile(path.toFile(), <span class=\"hljs-string\">\"r\"<\/span>);\nFileChannel channel = file.getChannel();<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>... oder \u00fcber einen <code>FileInputStream<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\">Path path = ...;\nFileInputStream in = <span class=\"hljs-keyword\">new<\/span> FileInputStream(path.toFile());\nFileChannel channel = in.getChannel();<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Welche Variante Du w\u00e4hlst, macht in diesem Beispiel keinen Unterschied. Im Endeffekt erstellen die <code>getChannel()<\/code>-Methoden einen neuen <code>FileChannel<\/code> anhand der im <code>RandomAccessFile<\/code> bzw. <code>FileInputStream<\/code> hinterlegten Datei-Informationen. Obwohl du also bspw. aus einem <code>FileInputStream<\/code> die Daten nur sequentiell lesen kannst, gilt diese Einschr\u00e4nkung nicht f\u00fcr den \u00fcber den <code>FileInputStream<\/code> erstellten <code>FileChannel<\/code>. <\/p>\n\n\n\n<p>Allerdings werden die <code>readable<\/code>- und <code>writable<\/code>-Flags entsprechend gesetzt:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Einen \u00fcber <code>FileInputStream.getChannel()<\/code> erzeugten <code>FileChannel<\/code> kannst du nur zum Lesen verwenden.<\/li>\n\n\n\n<li>Einen \u00fcber <code>RandomAccessFile.getChannel()<\/code>  erzeugten <code>FileChannel<\/code> kannst du zum Lesen und Schreiben verwenden. <\/li>\n\n\n\n<li>Wie du einen \u00fcber <code>FileChannel.<em>open<\/em>()<\/code> erstellten <code>FileChannel<\/code> verwenden kannst, bestimmst du \u00fcber die im zweiten Parameter \u00fcbergebenen Optionen. Da wir im Beispiel oben <code>StandardOpenOption.<strong><em>READ<\/em><\/strong><\/code> \u00fcbergeben haben, ist in diesem Fall nur Lesezugriff erlaubt. (Eine \u00dcbersicht der verf\u00fcgbaren Optionen findest Du im <a rel=\"noopener\" href=\"https:\/\/docs.oracle.com\/en\/java\/javase\/13\/docs\/api\/java.base\/java\/nio\/file\/StandardOpenOption.html\" target=\"_blank\">JavaDoc von StandardOpenOption<\/a>.)<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Lesen einer Datei mit FileChannel und ByteBuffer<\/h4>\n\n\n\n<p>Wenn du einen <code>FileChannel<\/code> ge\u00f6ffnet hast, kannst du daraus mit <code>FileChannel.read()<\/code> in einen <code>ByteBuffer<\/code> lesen. Das folgende Beispiel liest Bl\u00f6cke von jeweils 1.024 Bytes, gibt deren jeweilige L\u00e4nge sowie deren erstes und letztes Byte aus \u2013 bis das Ende der Datei erreicht ist:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\">Path path = Path.of(<span class=\"hljs-string\">\"read-demo.bin\"<\/span>);\n<span class=\"hljs-keyword\">try<\/span> (FileChannel channel = FileChannel.open(path,\n        StandardOpenOption.READ)) {\n  ByteBuffer buffer = ByteBuffer.allocate(<span class=\"hljs-number\">1024<\/span>);\n\n  <span class=\"hljs-keyword\">int<\/span> bytesRead;\n  <span class=\"hljs-keyword\">while<\/span> ((bytesRead = channel.read(buffer)) != -<span class=\"hljs-number\">1<\/span>) {\n    System.out.printf(<span class=\"hljs-string\">\"bytes read from file: %d%n\"<\/span>, bytesRead);\n    <span class=\"hljs-keyword\">if<\/span> (bytesRead &gt; <span class=\"hljs-number\">0<\/span>) {\n      System.out.printf(<span class=\"hljs-string\">\"  first byte: %d, last byte: %d%n\"<\/span>,\n              buffer.get(<span class=\"hljs-number\">0<\/span>), buffer.get(bytesRead - <span class=\"hljs-number\">1<\/span>));\n    }\n    buffer.rewind();\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Mit <code>channel.read(buffer)<\/code> lesen wir so viele Bytes wie m\u00f6glich aus der Datei und schreiben sie in den Buffer. Mit <code>buffer.get(index)<\/code> lesen wir einzelne Bytes aus dem Buffer, ohne dessen Leseposition vorher setzen zu m\u00fcssen und ohne sie zu ver\u00e4ndern. Mit <code>buffer.rewind()<\/code> m\u00fcssen wir am Ende der Schleife den Buffer \"zur\u00fcckspulen\", so dass er erneut gef\u00fcllt werden kann.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Lesen einer Datei mit ByteBuffer.flip() und compact()<\/h4>\n\n\n\n<p>Im folgenden, zweiten Beispiel gehen wir etwas anders vor. Hier lesen wir alle Bytes des Buffers und addieren sie. Anstatt auf die Daten mit <code>buffer.get(index)<\/code> zuzugreifen, verwenden wir zun\u00e4chst <code>buffer.flip()<\/code>, um die Leseposition auf den Anfang des Buffers zu setzen und danach <code>buffer.get()<\/code> um einzelne Byte von der aktuellen Leseposition zu lesen. <\/p>\n\n\n\n<p>Wir lesen nicht den gesamten Buffer aus, sondern nur eine zuf\u00e4llig Anzahl an Bytes, und simulieren damit, dass wir die gelesenen Daten nicht vollst\u00e4ndig verarbeiten k\u00f6nnen. Danach schalten wir mit <code>buffer.compact()<\/code> in den Buffer-Schreibmodus zur\u00fcck und lesen weitere Bytes aus dem <code>FileChannel<\/code>. Ich empfehle zum besseren Verst\u00e4ndnis den Artikel <a href=\"\/de\/java\/bytebuffer-flip-compact\/\">Java ByteBuffer: Wie funktionieren flip() und compact()<\/a> zu lesen.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\">Path path = Path.of(<span class=\"hljs-string\">\"read-demo.bin\"<\/span>);\n<span class=\"hljs-keyword\">try<\/span> (FileChannel channel = FileChannel.open(path,\n        StandardOpenOption.READ)) {\n  ByteBuffer buffer = ByteBuffer.allocate(<span class=\"hljs-number\">1024<\/span>);\n\n  <span class=\"hljs-keyword\">int<\/span> bytesRead;\n  <span class=\"hljs-keyword\">while<\/span> ((bytesRead = channel.read(buffer)) != -<span class=\"hljs-number\">1<\/span>) {\n    System.out.printf(<span class=\"hljs-string\">\"bytes read from file: %d%n\"<\/span>, bytesRead);\n\n    <span class=\"hljs-keyword\">long<\/span> sum = <span class=\"hljs-number\">0<\/span>;\n\n    buffer.flip();\n    <span class=\"hljs-keyword\">int<\/span> numBytesToRead =\n            ThreadLocalRandom.current().nextInt(buffer.remaining());\n    <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">int<\/span> i = <span class=\"hljs-number\">0<\/span>; i &lt; numBytesToRead; i++) {\n      sum += buffer.get();\n    }\n\n    System.out.printf(<span class=\"hljs-string\">\"  bytes read from buffer: %d, sum of bytes: %d%n\"<\/span>,\n            numBytesToRead, sum);\n    buffer.compact();\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In der Ausgabe sehen wir, dass beim ersten Aufruf von <code>channel.read()<\/code> 1.024 Bytes aus der Datei gelesen werden und bei jedem weiteren Aufruf genau so viele Bytes, wie wir zuvor aus dem Buffer ausgelesen haben (und entsprechend im Buffer wieder freier Platz zur Verf\u00fcgung geworden ist).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"wie-schreibt-man-eine-datei-mit-filechannel\">Wie schreibt man eine Datei mit FileChannel? <\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">\u00d6ffnen eines FileChannels zum Schreiben in eine Datei<\/h4>\n\n\n\n<p>Zum Schreiben einer Datei muss zun\u00e4chst wieder ein <code>FileChannel<\/code> ge\u00f6ffnet werden. Dies funktioniert analog zum Lesen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\">Path path = ...;\nFileChannel channel = FileChannel.open(path,\n    StandardOpenOption.CREATE, StandardOpenOption.WRITE);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Anstatt <code>StandardOpenOption.<strong><em>READ<\/em><\/strong><\/code> \u00fcbergeben wir hier <code>StandardOpenOption.<strong><em>WRITE<\/em><\/strong><\/code>. Zus\u00e4tzlich gebe ich im Beispiel <code>StandardOpenOption.<strong><em>CREATE<\/em><\/strong><\/code> mit an, so dass die Datei erstellt wird, falls sie nicht existiert.<\/p>\n\n\n\n<p>Weitere f\u00fcr den Schreibvorgang relevante Optionen sind:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>StandardOpenOption.<strong><em>CREATE_NEW<\/em><\/strong><\/code>: Die Datei wird erstellt, wenn sie noch nicht existiert; andernfalls wird eine <code>FileAlreadyExistsException<\/code> geworfen.<\/li>\n\n\n\n<li><code>StandardOpenOption.<strong><em>APPEND<\/em><\/strong><\/code>: Daten werden an die Datei angeh\u00e4ngt. Die Position 0 des <code>FileChannel<\/code>s entspricht dabei nicht der Position 0 innerhalb der Datei sondern der aktuellen L\u00e4nge der Datei.<\/li>\n\n\n\n<li><code>StandardOpenOption.<strong><em>TRUNCATE_EXISTING<\/em><\/strong><\/code>: Die Datei wird vor dem Schreiben komplett geleert. Diese Option kann nicht zusammen mit <strong><em><code>APPEND<\/code><\/em><\/strong> verwendet werden.<\/li>\n<\/ul>\n\n\n\n<p>Analog zum Lesen kannst Du einen beschreibbaren <code>FileChannel<\/code> auch \u00fcber <code>RandomAccessFile.getChannel()<\/code> oder <code>FileOutputStream.getChannel()<\/code> erstellen.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Schreiben in eine Datei mit ByteBuffer.flip() und compact()<\/h4>\n\n\n\n<p>Im folgenden Beispiel wird zehn mal eine zuf\u00e4lllige Anzahl an Bytes in den <code>ByteBuffer<\/code> geschrieben und dann von dort in den <code>FileChannel<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\">Path path = Path.of(<span class=\"hljs-string\">\"write-demo.bin\"<\/span>);\n<span class=\"hljs-keyword\">try<\/span> (FileChannel channel = FileChannel.open(path,\n        StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {\n\n  ByteBuffer buffer = ByteBuffer.allocate(<span class=\"hljs-number\">1024<\/span>);\n\n  <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">int<\/span> i = <span class=\"hljs-number\">0<\/span>; i &lt; <span class=\"hljs-number\">10<\/span>; i++) {\n    <span class=\"hljs-keyword\">int<\/span> bytesToWrite =\n            ThreadLocalRandom.current().nextInt(buffer.capacity());\n    <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">int<\/span> j = <span class=\"hljs-number\">0<\/span>; j &lt; bytesToWrite; j++) {\n      buffer.put((<span class=\"hljs-keyword\">byte<\/span>) ThreadLocalRandom.current().nextInt(<span class=\"hljs-number\">256<\/span>));\n    }\n\n    buffer.flip();\n    channel.write(buffer);\n    buffer.compact();\n  }\n\n  <span class=\"hljs-comment\">\/\/ channel.write() doesn't guarantee all data to be written to the channel.<\/span>\n  <span class=\"hljs-comment\">\/\/ If there are remaining bytes in the buffer, write them now.<\/span>\n  buffer.flip();\n  <span class=\"hljs-keyword\">while<\/span> (buffer.hasRemaining()) {\n    channel.write(buffer);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Die Schreibposition des Buffers steht zun\u00e4chst auf 0. Mit <code>buffer.put()<\/code> f\u00fcllen wir den <code>ByteBuffer<\/code> bis zu einer zuf\u00e4lligen Position. Mit <code>buffer.flip()<\/code> schalten wir in den Buffer-Lesemodus, mit <code>channel.write(buffer)<\/code> schreiben wir den Inhalt des Buffers in die Datei, und mit <code>buffer.compact()<\/code> schalten wir den Buffer zur\u00fcck in den Schreibmodus. <\/p>\n\n\n\n<p>Beim Aufruf von <code>channel.write(buffer)<\/code> wird nicht garantiert, dass der gesamte Inhalt des Buffers in den Channel geschrieben wird. Deshalb m\u00fcssen wir am Ende so lange <code>channel.write(buffer)<\/code> aufrufen, bis <code>buffer.hasRemaining()<\/code> den Wert <code>false<\/code> zur\u00fcckliefert, d. h. der Buffer keine weiteren Daten mehr enth\u00e4lt.<\/p>\n\n\n\n<p>Auch an dieser Stelle empfehle ich noch einmal zum besseren Verst\u00e4ndnis des ByteBuffers den Artikel <a href=\"\/de\/java\/bytebuffer-flip-compact\/\">Java ByteBuffer: Wie funktionieren flip() und compact()<\/a> zu lesen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"wie-liest-man-daten-von-bzw-schreibt-daten-an-einer-bestimmten-position\">Wie liest man Daten von bzw. schreibt Daten an einer bestimmten Position?<\/h3>\n\n\n\n<p>Die Lese- bzw. Schreibposition innerhalb eines Channels kann jederzeit mit <code>FileChannel.position()<\/code> gelesen und mit <code>FileChannel.position(newPosition)<\/code> neu gesetzt werden. Im folgenden Beispiel wird eine Datei von hinten nach vorne mit den Bytes 0xff bis 0x00 beschrieben. Danach wird der Inhalt an zehn zuf\u00e4lligen Positionen ausgelesen und auf dem Bildschirm ausgegeben.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\">Path path = Path.of(<span class=\"hljs-string\">\"position-demo.bin\"<\/span>);\n<span class=\"hljs-keyword\">try<\/span> (FileChannel channel = FileChannel.open(path,\n        StandardOpenOption.CREATE, StandardOpenOption.WRITE,\n        StandardOpenOption.READ)) {\n\n  ByteBuffer buffer = ByteBuffer.allocate(<span class=\"hljs-number\">1<\/span>);\n\n  <span class=\"hljs-comment\">\/\/ Write backwards<\/span>\n  <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">int<\/span> pos = <span class=\"hljs-number\">255<\/span>; pos &gt;= <span class=\"hljs-number\">0<\/span>; pos--) {\n    buffer.put((<span class=\"hljs-keyword\">byte<\/span>) pos);\n    buffer.flip();\n    channel.position(pos);\n    <span class=\"hljs-keyword\">while<\/span> (buffer.remaining() &gt; <span class=\"hljs-number\">0<\/span>) {\n      channel.write(buffer);\n    }\n    buffer.compact();\n  }\n\n  <span class=\"hljs-comment\">\/\/ Read from random positions<\/span>\n  <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">int<\/span> i = <span class=\"hljs-number\">0<\/span>; i &lt; <span class=\"hljs-number\">10<\/span>; i++) {\n    <span class=\"hljs-keyword\">long<\/span> pos = ThreadLocalRandom.current().nextLong(channel.size());\n    channel.position(pos);\n    channel.read(buffer);\n    buffer.flip();\n    <span class=\"hljs-keyword\">byte<\/span> b = buffer.get();\n    System.out.printf(<span class=\"hljs-string\">\"Byte at position %d: %d%n\"<\/span>, pos, b);\n    buffer.compact();\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Im Abschnitt \"Memory-mapped Files\" wirst du sehen, wie du dies deutlich eleganter machen kannst.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"wie-ermittelt-man-die-dateigroesse\">Wie ermittelt man die Dateigr\u00f6\u00dfe?<\/h3>\n\n\n\n<p>Die Dateigr\u00f6\u00dfe wird wie folgt ermittelt:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">long<\/span> fileSize = channel.size();<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"wie-aendert-man-die-dateigroesse\">Wie \u00e4ndert man die Dateigr\u00f6\u00dfe?<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">Wie vergr\u00f6\u00dfert man eine Datei?<\/h4>\n\n\n\n<p>Beim Schreiben in eine Datei wird die Datei automatisch vergr\u00f6\u00dfert, wenn du \u00fcber das Ende der Datei hinaus schreibst.<\/p>\n\n\n\n<p>Beispielsweise k\u00f6nnte man wie folgt (vorausgesetzt, sie existiert noch nicht) eine Datei mit 1 GB Gr\u00f6\u00dfe erstellen, die 2^30 - 1 Nullen enth\u00e4lt und eine Eins:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\">Path path = Path.of(<span class=\"hljs-string\">\"1g-demo.bin\"<\/span>);\n<span class=\"hljs-keyword\">try<\/span> (FileChannel channel = FileChannel.open(path,\n        StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {\n\n  ByteBuffer buffer = ByteBuffer.allocate(<span class=\"hljs-number\">1<\/span>);\n  buffer.put((<span class=\"hljs-keyword\">byte<\/span>) <span class=\"hljs-number\">1<\/span>);\n  buffer.flip();\n\n  channel.position((<span class=\"hljs-number\">1<\/span> &lt;&lt; <span class=\"hljs-number\">30<\/span>) - <span class=\"hljs-number\">1<\/span>);\n  channel.write(buffer);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h4 class=\"wp-block-heading\">Wie verkleinert man eine Datei?<\/h4>\n\n\n\n<p>Um eine Datei zu verkleinern, muss man <code>channel.truncate()<\/code> aufrufen. Im folgenden Beispiel wird die zuvor erstellte 1 GB-Datei auf 1 KB gek\u00fcrzt:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\">Path path = Path.of(<span class=\"hljs-string\">\"1g-demo.bin\"<\/span>);\n<span class=\"hljs-keyword\">try<\/span> (FileChannel channel = FileChannel.open(path,\n        StandardOpenOption.WRITE)) {\n  channel.truncate(<span class=\"hljs-number\">1<\/span> &lt;&lt; <span class=\"hljs-number\">10<\/span>);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Wenn die angegebene neue Gr\u00f6\u00dfe gr\u00f6\u00dfer oder gleich der aktuellen Gr\u00f6\u00dfe ist, hat der Aufruf von <code>truncate()<\/code> keine Auswirkung.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"wie-erzwingt-man-das-schreiben-aufs-speichermedium\">Wie erzwingt man das Schreiben aufs Speichermedium?<\/h3>\n\n\n\n<p>Aus Performance-Gr\u00fcnden cacht das Betriebssystem \u00c4nderungen an Dateien und schreibt diese normalerweise nicht sofort auf das Speichermedium. <\/p>\n\n\n\n<p>Mit <code>channel.force(boolean metaData)<\/code> kann das Betriebssystem veranlasst werden, die Daten unverz\u00fcglich zu schreiben. \u00dcber den Parameter <code>metaData<\/code> wird festgelegt, ob auch Metadaten (wie der Zeitpunkt der letzten \u00c4nderung und des letzten Zugriffs) auf das Speichermedium geschrieben werden sollen:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Gibt man hier <code>true<\/code> an, veranlasst die Methode auch das sofortige Schreiben der Metadaten, was zus\u00e4tzliche I\/O-Operationen erfordert und damit l\u00e4nger dauert.<\/li>\n\n\n\n<li>\u00dcbergibt man hier <code>false<\/code>, wird nur der Inhalt der Datei geschrieben.<\/li>\n<\/ul>\n\n\n\n<p>Es ist nicht garantiert, dass der Wert von <code>metaData<\/code> auf allen Betriebssystemen ber\u00fccksichtigt wird.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"memory-mapped-files-wie-man-einen-teil-einer-datei-in-den-speicher-mappt\">Memory-mapped Files: Wie man einen Teil einer Datei in den Speicher mappt<\/h2>\n\n\n\n<p>Eine besondere Art von <code>ByteBuffer<\/code> ist der <code>MappedByteBuffer<\/code> \u2013 dabei wird ein Teil einer Datei direkt in den Arbeitsspeicher gemappt (auf englisch: \"memory-mapped file\"). Dies erlaubt einen besonders effizienten Zugriff auf die Datei ohne Umwege \u00fcber <code>FileChannel.write()<\/code> und <code>read()<\/code>. Auf den <code>MappedByteBuffer<\/code> kann man wie auf ein Byte-Array zugreifen, d. h. an beliebigen Stellen schreiben und von beliebigen Stellen lesen. \u00c4nderungen werden im Hintergrund transparent in die Datei geschrieben.<\/p>\n\n\n\n<p>Das direkte Mapping bedeutet einen enormen Performance-Gewinn gegen\u00fcber herk\u00f6mmlichem Lesen und Schreiben. Die Datei wird direkt in den \"User Space\" des Arbeitsspeichers gemappt. Hingegen m\u00fcssen bei der herk\u00f6mmlichen Variante Daten zwischen \"Kernel Space\" und \"User Space\" hin- und herkopiert werden.<\/p>\n\n\n\n<p>Im folgenden wird das Beispiel aus dem Abschnitt <a href=\"#Wie_liest_man_Daten_von_bzw_schreibt_Daten_an_einer_bestimmten_Position\">Wie liest man Daten von bzw. schreibt Daten an einer bestimmten Position?<\/a>, in dem eine Datei von hinten nach vorne beschrieben und dann an zuf\u00e4lligen Positionen gelesen wurde, mit einem <code>MappedByteBuffer<\/code> \u2013 deutlich eleganter \u2013 neu geschrieben:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\">Path path = Path.of(<span class=\"hljs-string\">\"mapped-byte-buffer-demo.bin\"<\/span>);\n<span class=\"hljs-keyword\">try<\/span> (FileChannel channel = FileChannel.open(path,\n        StandardOpenOption.CREATE, StandardOpenOption.WRITE,\n        StandardOpenOption.READ)) {\n  MappedByteBuffer buffer =\n          channel.map(FileChannel.MapMode.READ_WRITE, <span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">256<\/span>);\n\n  <span class=\"hljs-comment\">\/\/ Write backwards<\/span>\n  <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">int<\/span> pos = <span class=\"hljs-number\">255<\/span>; pos &gt;= <span class=\"hljs-number\">0<\/span>; pos--) {\n    buffer.put(pos, (<span class=\"hljs-keyword\">byte<\/span>) pos);\n  }\n\n  <span class=\"hljs-comment\">\/\/ Read from random positions<\/span>\n  <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">int<\/span> i = <span class=\"hljs-number\">0<\/span>; i &lt; <span class=\"hljs-number\">10<\/span>; i++) {\n    <span class=\"hljs-keyword\">int<\/span> pos = ThreadLocalRandom.current().nextInt((<span class=\"hljs-keyword\">int<\/span>) channel.size());\n    <span class=\"hljs-keyword\">byte<\/span> b = buffer.get(pos);\n    System.out.printf(<span class=\"hljs-string\">\"Byte at position %d: %d%n\"<\/span>, pos, b);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"besonderheiten-von-memory-mapped-files\">Besonderheiten von Memory-mapped Files<\/h3>\n\n\n\n<p>Folgendes ist bei der Verwendung von memory-mapped Files zu beachten:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Die Position und Gr\u00f6\u00dfe des zu mappenden Bereichs muss zu Beginn angegeben werden. Im Beispiel werden die ersten 256 Bytes gemappt. Wenn die Datei nicht existiert, wird eine 256 Byte gro\u00dfe Datei erstellt. Existiert die Datei und ist kleiner, wird sie auf 256 Byte vergr\u00f6\u00dfert. Ist die Datei gr\u00f6\u00dfer, bleibt ihre Gr\u00f6\u00dfe sowie der Inhalt nach den ersten 256 Bytes unver\u00e4ndert.<\/li>\n\n\n\n<li>Es kann maximal ein 2 GB gro\u00dfer Bereich in den Speicher gemappt werden. Als der <code>MappedByteBuffer<\/code> mit Java 1.4 im Jahr 2002 ver\u00f6ffentlicht wurde, konnten sich die Java-Entwickler offenbar nicht vorstellen, dass heute fast jeder Developer-Laptop mit 16 bis 32 GB RAM best\u00fcckt ist. Bis einschlie\u00dflich Java 14 wurde dieses Limit nicht erh\u00f6ht.<\/li>\n\n\n\n<li>Der <code>MappedByteBuffer<\/code> implementiert <em>nicht<\/em> das <code>Closeable<\/code>-Interface. Deshalb k\u00f6nnen wir ihn im Beispiel oben auch nicht im <code>try<\/code>-Block erstellen. Es gibt auch keine Methode, um ihn manuell zu \"un-mappen\". Wenn wir am Ende des Beispiels oben versuchen w\u00fcrden die Datei zu l\u00f6schen, bek\u00e4men wir in den meisten F\u00e4llen eine <code>AccessDeniedException<\/code>. Der <code>MappedByteBuffer<\/code> wird vom Garbage Collector entfernt, wenn er nicht mehr gebraucht wird. Um die Datei zu \"un-mappen\" registriert er einen sogenanten \"Cleaner\", der aufgerufen wird, wenn der <code>MappedByteBuffer<\/code> nur noch \"phantom reachable\" ist. Im Code des weiter unten beschriebenen Performance-Tests findet ihr einen Hack mit <code>sun.misc.Unsafe<\/code>, um die Datei manuell zu \"un-mappen\".<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"erstellung-eines-mappedbytebuffer-von-einem-fileinputstream-fileoutputstream\">Erstellung eines MappedByteBuffer von einem FileInputStream \/ FileOutputStream<\/h3>\n\n\n\n<p>Wir haben oben gesehen, dass wir auch aus einem <code>FileInputStream<\/code> oder einem <code>FileOutputStream<\/code> mit <code>getChannel()<\/code> einen <code>FileChannel<\/code> erzeugen k\u00f6nnen. Was passiert, wenn wir versuchen, diesen in den Speicher zu mappen?<\/p>\n\n\n\n<p>Da der <code>FileChannel<\/code> quasi unabh\u00e4ngig vom <code>FileInputStream<\/code> bzw. <code>FileOutputStream<\/code> ist, ist folgendes problemlos m\u00f6glich:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">var<\/span> fis = <span class=\"hljs-keyword\">new<\/span> FileInputStream(fileName);\n<span class=\"hljs-keyword\">var<\/span> channel = fis.getChannel();\n<span class=\"hljs-keyword\">var<\/span> map = channel.map(FileChannel.MapMode.READ_ONLY, <span class=\"hljs-number\">0<\/span>, channel.size());<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Was allerdings nicht funktioniert ist folgendes:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">var<\/span> fos = <span class=\"hljs-keyword\">new<\/span> FileOutputStream(fileName);\n<span class=\"hljs-keyword\">var<\/span> channel = fos.getChannel();\n<span class=\"hljs-keyword\">var<\/span> map = channel.map(FileChannel.MapMode.READ_WRITE, <span class=\"hljs-number\">0<\/span>, channel.size());<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Das Kommando <code>channel.map()<\/code> f\u00fchrt hierbei zu einer <code>NonReadableChannelException<\/code>, da der durch <code>FileOutputStream.getChannel()<\/code> erzeugte <code>FileChannel<\/code> nur ein Schreiben erlaubt \u2013 der <code>MapMode.READ_WRITE<\/code> hingegen auch einen Lesezugriff erfordert. Einen <code>MapMode.WRITE_ONLY<\/code> gibt es nicht.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"file-locking-sperren-von-dateibereichen\">File Locking: Sperren von Dateibereichen<\/h2>\n\n\n\n<p>Bei komplexeren Anwendungen (z. B. einem File- oder Datenbankserver) m\u00f6chte man ggf. von verschiedenen Threads oder auch Prozessen auf ein- und dieselbe Datei zugreifen. Dabei m\u00fcssen ganze Dateien oder Bereiche von Dateien, in die gerade geschrieben wird, gelockt werden, so dass keine anderen Threads oder Prozesse auf denselben Bereich zugreifen k\u00f6nnen.<\/p>\n\n\n\n<p>Das Locking wird dabei direkt vom Betriebssystem und dem Filesystem unterst\u00fctzt, so dass dies auch zwischen mehreren Java-Programmen funktioniert bzw. zwischen Java-Programmen und beliebigen anderen Prozessen auf dem gleichen System \u2013 oder bei Verwendung eines Shared Storages \u2013 z. B. eines Netzwerklaufwerks \u2013 auch zwischen Prozessen auf unterschiedlichen Systemen.<\/p>\n\n\n\n<p>Man unterscheidet zwischen shared Locks (\"read locks\") und exclusive Locks (\"write locks\"). Wenn ein Prozess einen exklusiven Lock auf einen Dateibereich h\u00e4lt, kann kein anderer Prozess einen Lock auf denselben oder einen \u00fcberlappenden Dateibereich bekommen \u2013 weder einen exklusiven noch einen shared Lock. Wenn ein Prozess einen shared Lock h\u00e4lt, k\u00f6nnen andere Prozesse ebenfalls einen shared Lock auf denselben oder einen \u00fcberlappenden Dateibereich erhalten.<\/p>\n\n\n\n<p>Mit folgenden Methoden kannst du ein Lock setzen:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>FileChannel.lock(position, size, shared)<\/code> \u2013 die Methode wartet so lange, bis f\u00fcr den durch <code>position<\/code> und <code>size<\/code> angegebenen Bereich ein Lock des gew\u00fcnschten Typs (<code>shared = true<\/code> \u2192 shared; <code>shared = false<\/code> \u2192 exclusive) gesetzt werden kann.<\/li>\n\n\n\n<li><code>FileChannel.lock()<\/code> \u2013 die Methode wartet, bis f\u00fcr die gesamte Datei ein exklusives Lock gesetzt werden kann.<\/li>\n\n\n\n<li><code>FileChannel.tryLock(position, size, shared)<\/code> \u2013 die Methode versucht f\u00fcr den angegebenenn Bereich ein Lock des gew\u00fcnschten Typs zu setzen. Ist dies nicht m\u00f6glich, wartet sie nicht, sondern gibt <code>null<\/code> zur\u00fcck.<\/li>\n\n\n\n<li><code>FileChannel.tryLock()<\/code> \u2013 die Methode versucht f\u00fcr die gesamte Datei ein exklusives Lock zu setzen. Ist dies nicht m\u00f6glich, gibt sie <code>null<\/code> zur\u00fcck.<\/li>\n<\/ul>\n\n\n\n<p>Bei erfolgreichem Setzen des Locks geben die Methoden ein <code>FileLock<\/code>-Objekt zur\u00fcck, \u00fcber dessen <code>release()<\/code>- oder <code>close()<\/code>-Methode das Lock wieder freigegeben werden kann. Hier einfaches Beispiel, das ein exklusives Lock auf die komplette Datei setzt und dann 1.000 zuf\u00e4llige Bytes schreibt:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\">Path path = Path.of(<span class=\"hljs-string\">\"lock-demo.bin\"<\/span>);\n\n<span class=\"hljs-keyword\">byte<\/span>&#091;] bytes = <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-keyword\">byte<\/span>&#091;<span class=\"hljs-number\">1000<\/span>];\nThreadLocalRandom.current().nextBytes(bytes);\n\n<span class=\"hljs-keyword\">try<\/span> (FileChannel channel = FileChannel.open(path,\n        StandardOpenOption.CREATE, StandardOpenOption.WRITE);\n     FileLock lock = channel.lock()) {\n  ByteBuffer buffer = ByteBuffer.wrap(bytes);\n  channel.write(buffer);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"performance-tests\">Performance Tests<\/h2>\n\n\n\n<p>Ich habe ein Programm geschrieben, um die Performance der verschiedenen Schreibmethoden zu messen \u2013 bei unterschiedlichen Buffer- und Dateigr\u00f6\u00dfen.<\/p>\n\n\n\n<p>Um ein m\u00f6glichst seiteneffektfreies Ergebnis zu erhalten, habe ich jeden Test 32 mal wiederholt und dann den Median ermittelt. Dabei habe ich Dateien von 1 MB bis 1 GB Gr\u00f6\u00dfe erstellt und f\u00fcr den ByteBuffer zwischen 1 KB und 1 MB zur Verf\u00fcgung gestellt.<\/p>\n\n\n\n<p>Alle Tests werden ohne <code>force()<\/code> durchgef\u00fchrt. Ich m\u00f6chte die Geschwindkeit testen, mit der die Daten ans Betriebssystem \u00fcbermittelt werden, nicht die Geschwindkeit des Storage-Mediums.<\/p>\n\n\n\n<p>Das Testprogramm kannst du gerne von meinem <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/filechannel-bytebuffer-memory-mapped-io-locks\" target=\"_blank\">GitHub-Repository<\/a> clonen und auf deinem System laufen lassen.<\/p>\n\n\n\n<p>Im Quellcode des Testprogramms kannst du sehen, dass die Tests auch f\u00fcr solche <code>FileChannel<\/code> ausgef\u00fchrt werden, die \u00fcber <code>RandomAccessFile.getChannel()<\/code> und <code>FileOutputStream.getChannel()<\/code> erzeugt werden. Da die Ergebnisse jeweils fast identisch zu denen von <code>FileChannel.open()<\/code> sind, zeige ich sie im folgenden nicht mit an.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"testergebnisse\">Testergebnisse<\/h3>\n\n\n\n<p>Die Testergebnisse sind zu umfangreich, um sie hier komplett abzudrucken. Du kannst sie dir gerne in diesem <a href=\"https:\/\/docs.google.com\/spreadsheets\/d\/1OkrQVIgEPjXfEIapsSJS1m59xjte3GZC2YeJcNq_728\/edit\" target=\"_blank\" rel=\"noopener\">Google Document<\/a> anschauen.<\/p>\n\n\n\n<p>In erster Linie h\u00e4ngt die Schreibgeschwindkeit von der Art des Zugriffs ab \u2013 sequentiell oder random access. Interessant ist, dass auch Buffer- und Dateigr\u00f6\u00dfe einen erheblichen Einfluss auf das Resultat haben. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Testergebnisse sequentieller Schreibzugriff<\/h4>\n\n\n\n<p>Bei sequentiellem Schreibzugriff steigt die Geschwindigkeit bis zu einer Dateigr\u00f6\u00dfe von 128 MB kontinuerlich an, danach stagniert oder sinkt sie. Ich vermute, dass ab dieser Gr\u00f6\u00dfe das Betriebssystem beginnt die Daten auf das Storage-Medium zu schreiben, ab hier also dessen Geschwindigkeit in die Messergebnisse mit eingeht. Ich werde daher im folgenden nur Messwerte bis zur Dateigr\u00f6\u00dfe von 128 MB darstellen.<\/p>\n\n\n\n<p>Im folgenden siehst du vier Diagramme, die f\u00fcr die Dateigr\u00f6\u00dfen 1 MB, 8 MB, 16 MB und 128 MB und jeweils vier Schreibmethoden die Schreibgeschwindigkeit in Abh\u00e4ngigkeit von der Buffergr\u00f6\u00dfe zeigen.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"440\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_1MB-800x440.png\" alt=\"Sequentielle Datei-Schreibgeschwindkeit f\u00fcr 1 MB gro\u00dfe Dateien\" class=\"wp-image-10116\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_1MB-800x440.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_1MB-224x123.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_1MB-336x185.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_1MB-504x277.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_1MB-672x370.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_1MB-400x220.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_1MB-600x330.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_1MB-944x520.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_1MB.png 990w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Sequentielle Datei-Schreibgeschwindkeit f\u00fcr 1 MB gro\u00dfe Dateien<\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"440\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_8MB-800x440.png\" alt=\"Sequentielle Datei-Schreibgeschwindkeit f\u00fcr 8 MB gro\u00dfe Dateien\" class=\"wp-image-10117\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_8MB-800x440.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_8MB-224x123.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_8MB-336x185.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_8MB-504x277.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_8MB-672x370.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_8MB-400x220.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_8MB-600x330.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_8MB-944x520.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_8MB.png 990w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Sequentielle Datei-Schreibgeschwindkeit f\u00fcr 8 MB gro\u00dfe Dateien<\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"440\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_16MB-800x440.png\" alt=\"Sequentielle Datei-Schreibgeschwindkeit f\u00fcr 16 MB gro\u00dfe Dateien\" class=\"wp-image-10118\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_16MB-800x440.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_16MB-224x123.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_16MB-336x185.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_16MB-504x277.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_16MB-672x370.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_16MB-400x220.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_16MB-600x330.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_16MB-944x520.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_16MB.png 990w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Sequentielle Datei-Schreibgeschwindkeit f\u00fcr 16 MB gro\u00dfe Dateien<\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"440\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_128MB-800x440.png\" alt=\"Sequentielle Datei-Schreibgeschwindkeit f\u00fcr 128 MB gro\u00dfe Dateien\" class=\"wp-image-10121\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_128MB-800x440.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_128MB-224x123.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_128MB-336x185.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_128MB-504x277.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_128MB-672x370.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_128MB-400x220.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_128MB-600x330.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_128MB-944x520.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_128MB.png 990w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Sequentielle Datei-Schreibgeschwindkeit f\u00fcr 128 MB gro\u00dfe Dateien<\/figcaption><\/figure>\n<\/div>\n\n\n<h4 class=\"wp-block-heading\">Testergebnisse sequentieller Schreibzugriff \u2013 Analyse<\/h4>\n\n\n\n<p>Bei Dateien bis zu 8 MB Gr\u00f6\u00dfe sind memory-mapped Files \u2013 unabh\u00e4ngig von der Buffergr\u00f6\u00dfe \u2013 am schnellsten.<\/p>\n\n\n\n<p>Bei einer Dateigr\u00f6\u00dfe  von 16 MB gilt dies nur noch bis zu einer Buffergr\u00f6\u00dfe von 16 KB. Ab einer Buffergr\u00f6\u00dfe von 32 KB ist der <code>FileChannel<\/code> mit einem nativen <code>ByteBuffer<\/code> schneller. Bei einer Dateigr\u00f6\u00dfe von 128 MB ist der <code>FileChannel<\/code> bereits ab einer Buffergr\u00f6\u00dfe von 16 KB schneller.<\/p>\n\n\n\n<p>Der native <code>ByteBuffer<\/code> ist bis zu 20 % schneller als der Heap-<code>ByteBuffer<\/code>. Je gr\u00f6\u00dfer Datei und Buffer sind, desto gr\u00f6\u00dfer ist auch der Performance-Gewinn durch den nativen Buffer.<\/p>\n\n\n\n<p>Bis zu einer Buffergr\u00f6\u00dfe von 8 KB ist der <code>FileOutputStream<\/code> mit dem <code>BufferedOutputStream<\/code> schneller als der <code>FileChannel<\/code>. Ab 8 KB Buffergr\u00f6\u00dfe sind Stream und Channel mit Heap-Buffer etwa gleich schnell. Die Grenze von 8 KB ist auf den internen 8 KB gro\u00dfen Buffer des <code>BufferedOutputStream<\/code> zur\u00fcckzuf\u00fchren. Dieser f\u00fcllt erst den Buffer, bevor er die Daten in die Datei schreibt.<\/p>\n\n\n\n<p>Ab 1 MB Buffergr\u00f6\u00dfe geht die Geschwindigkeit bei allen Schreibmethoden und Dateigr\u00f6\u00dfen wieder zur\u00fcck.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Testergebnisse Random Write Access<\/h4>\n\n\n\n<p>Es folgen drei Diagramme, die f\u00fcr die Dateigr\u00f6\u00dfen 1 MB, 8 MB und 128 MB und jeweils drei Schreibmethoden die Random-Access-Schreibgeschwindigkeit in Abh\u00e4ngigkeit von der Buffergr\u00f6\u00dfe zeigen. Gr\u00f6\u00dfere Dateien habe ich nicht geschrieben, da die Random Write Access Tests im Allgemeinen deutlich l\u00e4nger dauern als die Tests des sequentiellen Schreibzugriffs.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"440\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_1MB-800x440.png\" alt=\"Random Access Datei-Schreibgeschwindkeit f\u00fcr 1 MB gro\u00dfe Dateien\" class=\"wp-image-10132\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_1MB-800x440.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_1MB-224x123.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_1MB-336x185.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_1MB-504x277.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_1MB-672x370.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_1MB-400x220.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_1MB-600x330.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_1MB-944x520.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_1MB.png 990w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Random Access Datei-Schreibgeschwindkeit f\u00fcr 1 MB gro\u00dfe Dateien<\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"440\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_8MB-800x440.png\" alt=\"Random Access Datei-Schreibgeschwindkeit f\u00fcr 8 MB gro\u00dfe Dateien\" class=\"wp-image-10133\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_8MB-800x440.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_8MB-224x123.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_8MB-336x185.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_8MB-504x277.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_8MB-672x370.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_8MB-400x220.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_8MB-600x330.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_8MB-944x520.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_8MB.png 990w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Random Access Datei-Schreibgeschwindkeit f\u00fcr 8 MB gro\u00dfe Dateien<\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"440\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_128MB-800x440.png\" alt=\"Random Access Datei-Schreibgeschwindkeit f\u00fcr 128 MB gro\u00dfe Dateien\" class=\"wp-image-10134\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_128MB-800x440.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_128MB-224x123.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_128MB-336x185.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_128MB-504x277.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_128MB-672x370.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_128MB-400x220.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_128MB-600x330.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_128MB-944x520.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/Java_file_write_performance_random_128MB.png 990w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Random Access Datei-Schreibgeschwindkeit f\u00fcr 128 MB gro\u00dfe Dateien<\/figcaption><\/figure>\n<\/div>\n\n\n<h4 class=\"wp-block-heading\">Testergebnisse Random Write Access \u2013 Analyse<\/h4>\n\n\n\n<p>Bei Random Write Access ist der Schreibzugriff \u2013 unabh\u00e4ngig von Datei- und Buffergr\u00f6\u00dfe \u2013 mit memory-mapped Files am schnellsten. <code>FileChannel<\/code> folgt mit gro\u00dfem Abstand, der Performancegewinn durch native Buffer kann auch hier bis zu 20% erreichen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"schlussfolgerung\">Schlussfolgerung<\/h3>\n\n\n\n<p>Bei Random Write Access sollte die Wahl immer auf memory-mapped Files fallen.<\/p>\n\n\n\n<p>Bei sequentiellem Schreibzugriff kann man bei Dateigr\u00f6\u00dfen bis zu 8 MB ebenfalls grunds\u00e4tzlich mit memory-mapped Files arbeiten. Bei gr\u00f6\u00dferen Dateien erreicht man die beste Performance mit <code>FileChannel<\/code> und mindestens 16 KB, maximal 512 KB gro\u00dfem, nativen <code>ByteBuffer<\/code>.<\/p>\n\n\n\n<p>Selbstverst\u00e4ndlich sind das nur grobe Richtwerte, abgeleitet aus Messergebnissen meines Systems. Wenn du die Performance bis auf's letzte MB\/s tunen willst, empfehle ich dir, f\u00fcr deinen speziellen Use Case verschiedene Schreibmethoden und Buffergr\u00f6\u00dfen zu testen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"zusammenfassung\">Zusammenfassung<\/h2>\n\n\n\n<p>Im heutigen Artikel habe ich dir gezeigt, was <code>FileChannel<\/code> und <code>ByteBuffer<\/code> sind und wie du damit Dateien schreibst und liest. Du hast gelernt, was memory-mapped Files sind und wie man Locks auf bestimmte Bereiche einer Datei setzen kann, so dass diese nicht gleichzeitig aus anderen Prozessen beschrieben werden k\u00f6nnen.<\/p>\n\n\n\n<p>Damit endet die sechsteilige Serie \u00fcber Dateien in Java. Wenn dir dieser Artikel (oder die ganze Serie) gefallen hat, teile ihn gerne \u00fcber einen der Share-Buttons unten. Wenn du \u00fcber neue Artikel informiert werden m\u00f6chtest, <a href=\"#\" data-formkit-toggle=\"d8ee997126\">klicke hier<\/a>, um dich f\u00fcr den HappyCoders-Newsletter anzumelden.<\/p>\n\n\n\n<p>Ich w\u00fcrde gerne von dir wissen: Welcher Teil dieser Serie hat dir am meisten geholfen oder am besten gefallen? Schreibe mir einen Kommentar!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In diesem Artikel lernst du alles \u00fcber die in Java 1.4 eingef\u00fchrten NIO-Klassen FileChannel und ByteBuffer.<\/p>\n<p>Du erf\u00e4hrst, welche M\u00f6glichkeiten sie zum Lesen und Schreiben von Dateien bieten und welche Vorteile sie \u2013 im Vergleich zu den zuvor besprochenen Methoden \u2013 haben.<\/p>\n","protected":false},"author":1,"featured_media":34373,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_seopress_titles_title":"","_seopress_titles_desc":"Wie funktionieren FileChannel und ByteBuffer? Was ist ein memory-mapped File? Wie setzt man Locks auf Dateibereiche?","_seopress_robots_index":"","_seopress_robots_follow":"","_seopress_robots_imageindex":"","_seopress_robots_snippet":"","_seopress_robots_primary_cat":"none","_seopress_robots_breadcrumbs":"","_seopress_robots_freeze_modified_date":"","_seopress_robots_custom_modified_date":"","_seopress_robots_canonical":"","_seopress_social_fb_title":"","_seopress_social_fb_desc":"","_seopress_social_fb_img":"","_seopress_social_fb_img_attachment_id":0,"_seopress_social_fb_img_width":0,"_seopress_social_fb_img_height":0,"_seopress_social_twitter_title":"","_seopress_social_twitter_desc":"","_seopress_social_twitter_img":"","_seopress_social_twitter_img_attachment_id":0,"_seopress_social_twitter_img_width":0,"_seopress_social_twitter_img_height":0,"_seopress_redirections_value":"","_seopress_redirections_enabled":"","_seopress_redirections_enabled_regex":"","_seopress_redirections_logged_status":"both","_seopress_redirections_param":"","_seopress_redirections_type":301,"_seopress_analysis_target_kw":"FileChannel,ByteBuffer","_seopress_news_disabled":"","_seopress_video_disabled":"","_seopress_video":[{"url":"","title":"","desc":"","thumbnail":"","duration":"","rating":"","view_count":"","tag":"","cat":""}],"_seopress_pro_schemas_manual":[{"_seopress_pro_rich_snippets_type":"none"}],"_seopress_pro_rich_snippets_disable_all":"","_seopress_pro_rich_snippets_disable":[],"_seopress_pro_schemas":[],"_uag_custom_page_level_css":"","_wp_convertkit_post_meta":{"form":"-1","landing_page":"","tag":"0","restrict_content":"0"},"_metis_text_type":"standard","_metis_text_length":27034,"_post_count":0,"footnotes":""},"categories":[64],"tags":[166],"class_list":["post-9864","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-java","tag-java-dateien"],"uagb_featured_image_src":{"full":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel.jpg",1770,986,false],"thumbnail":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel.jpg",150,84,false],"medium":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel.jpg",300,167,false],"medium_large":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel.jpg",768,428,false],"large":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel.jpg",1024,570,false],"feature_thumb_224":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel-224x125.jpg",224,125,true],"feature_thumb_336":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel-336x187.jpg",336,187,true],"feature_thumb_504":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel-504x281.jpg",504,281,true],"feature_thumb_672":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel-672x374.jpg",672,374,true],"half_400":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel-400x223.jpg",400,223,true],"half_600":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel-600x334.jpg",600,334,true],"full_800":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel-800x446.jpg",800,446,true],"full_944":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel-944x526.jpg",944,526,true],"full_1200":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel-1200x668.jpg",1200,668,true],"wide_1180":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel-1180x490.jpg",1180,490,true],"wide_1770":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel-1770x735.jpg",1770,735,true],"1536x1536":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel.jpg",1536,856,false],"2048x2048":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/02\/java-file-channel.jpg",1770,986,false]},"uagb_author_info":{"display_name":"Sven Woltmann","author_link":"https:\/\/www.happycoders.eu\/de\/author\/sven\/"},"uagb_comment_info":0,"uagb_excerpt":"In diesem Artikel lernst du alles \u00fcber die in Java 1.4 eingef\u00fchrten NIO-Klassen FileChannel und ByteBuffer. Du erf\u00e4hrst, welche M\u00f6glichkeiten sie zum Lesen und Schreiben von Dateien bieten und welche Vorteile sie \u2013 im Vergleich zu den zuvor besprochenen Methoden \u2013 haben.","public_identification_id":"40b3e3d9def749fdbe62f422a9f28b09","private_identification_id":"1e736b60a1064d06bb05db28ae417493","_links":{"self":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/9864","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/comments?post=9864"}],"version-history":[{"count":10,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/9864\/revisions"}],"predecessor-version":[{"id":41971,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/9864\/revisions\/41971"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/media\/34373"}],"wp:attachment":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/media?parent=9864"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/categories?post=9864"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/tags?post=9864"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}