# Java7: Faszination File AIO ...



## tuxedo (3. Feb 2012)

Hallo zusammen,

ausnahmsweise hab ich mal keine Frage ...

Evtl. gehört's in die Plauderecke. Aber vom Thema her passt bei den "Java-Themen" schon...

Hab eben gerade ein wenig mit Asynchronous File IO in Java 7 gespielt...:applaus:
Und ich bin total geflasht.

Folgendes "sinnloses Test-Szenario":

HashMap<Integer, Long> mit 20Mio Einträgen wird in eine File "persistiert".

An den Anfang der File schreib ich noch in einem 8-byte Long die Größe der Hashmap.

Ergo:

8 byte + (20Mio * (4 byte Integer + 8 byte Long)) ...

Das sind also insgesamt 240000008 bytes, oder rund 240MB.
Geschrieben wird auf:

* Samsung SATA2 1TB Platte
welche hier drin steckt:
* Intel i7 860 4x2,8Ghz
* 8GB RAM
* Debian 6 AMD64

Damit ich nicht Byte-Weise schreibe, schreib ich immer 1Mio Einträge der HashMap (12byte/Eintrag) in einen ByteBuffer und schiebe den dann zum schreiben auf die Platte runter.

Und das dauert "wahnsinnige" 1196ms .... 20Mio Einträge mit 240MB... nur 1,2sek... *hammer* :toll:

Kein Plan wie ich 240B/sec mit der Platte erreichen soll. Wohlmöglich ist Java der meinung die Daten wären schon fertig geschrieben und ist "fertig mit Zeit messen", wobei das OS noch 1-2sekunden lang den Schreib-Puffer leert oder sowas...
Sehen kann ich die 1-2sek jedenfalls nirgendwo. Im Filebrowser sehe ich die File die 240MB erreichen qausi im selben Moment wie das Test-Programm fertig ist.


Egal. Interessant ist eben: 

Kaum Aufwand in Java. Weder zeitmäßig noch implementierungsmäßig. Mit einem Profiler findet man eigentlich nix was man profilen könnte. Wird alles in den Tiefen, bzw. unterhalb der JVM erledigt.

Wer will da noch normales IO ???:L


----------



## tfa (3. Feb 2012)

Cool. Zeig mal den Quelltext.


----------



## tuxedo (3. Feb 2012)

tfa hat gesagt.:


> Cool. Zeig mal den Quelltext.



Kommt nach der MIttagspause. Muss den erst noch "herrichten"...

- Alex


----------



## c_sidi90 (3. Feb 2012)

240MB mit Java in 1,2 Sekunden. Da bin ich mal auf den Source gespannt, dass würde einige transaktionsgesteurte Ouputs vermeiden können.


----------



## tuxedo (3. Feb 2012)

So, hab noch ein wenig dran geschraubt und die File auch wieder probeweise eingelesen und den Inhalt verifiziert... Damit mir der Heap nicht gleich um die Ohren fliegt, musste ich von 20mio auf 10mio runter gehen: brauch jetzt 2 maps: eine initiell, die andere zum lesen (um einen vergleich zum zweiten befüllen zu haben).

Die Geschwindigekti ist aber nach wie vor die gleiche beim schreiben...

Hier der Source:


```
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.Semaphore;

/**
 *
 * @author achr
 */
public class Java7FileAioTest {

    private int count = 10000000; // write a total of 10mio entries
    private int setSize = 12;
    private int blockFactor = 1000000;
    private ByteBuffer buffer = ByteBuffer.allocate(blockFactor * setSize); // write 1mio entries with 12 bytes each at once
    private final HashMap<Integer, Long> map1;
    private final HashMap<Integer, Long> map2;
    private Iterator<Integer> iterator;
    private AsynchronousFileChannel afc;
    private Semaphore sema = new Semaphore(1);
    long readMapSize = -1;
    long readPos = 0;
    long fileSize = -1;
    private long start;
    private long stop;
    private CompletionHandler<Integer, AsynchronousFileChannel> writeSetCompletion = new CompletionHandler<Integer, AsynchronousFileChannel>() {

        @Override
        public void completed(Integer result, AsynchronousFileChannel attachment) {
            writeSet(attachment);
        }

        @Override
        public void failed(Throwable exc, AsynchronousFileChannel attachment) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    };
    private CompletionHandler<Integer, AsynchronousFileChannel> readSetCompletion = new CompletionHandler<Integer, AsynchronousFileChannel>() {

        @Override
        public void completed(Integer result, AsynchronousFileChannel attachment) {
            readPos += result;
            readSet(attachment);
        }

        @Override
        public void failed(Throwable exc, AsynchronousFileChannel attachment) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    };

    public Java7FileAioTest() {


        start = System.currentTimeMillis();
        map1 = new HashMap<>(count); // init hashmap mit voller größe --> geht dann schneller beim befüllen
        map2 = new HashMap<>(count); // init hashmap mit voller größe --> geht dann schneller beim befüllen
        stop = System.currentTimeMillis();

        System.out.println("Creating map: " + ((stop - start)/2) + "ms");

        long fillDuration = 0;
        long value = 0;
        for (int key = 0; key < count; key++) {
            if (key % 1000000 == 0) {
                System.out.println("Adding key: " + key);
            }
            start = System.nanoTime();
            map1.put(key, value);
            stop = System.nanoTime();
            fillDuration += (stop - start);
            value++;
        }
        System.out.println("Filling map takes " + (fillDuration / 1000000) + "ms");


        System.out.println("Start writing...");
        start = System.currentTimeMillis();
        writeToFile();
        stop = System.currentTimeMillis();
        System.out.println("Writing takes " + (stop - start) + " ms");
        System.out.println("Write-Speed: "+((double)fileSize/(double)((stop-start)/1000d))/1000000d+" mb/sec");

        map1.clear();

        System.out.println("Start reading + filling map...");
        start = System.currentTimeMillis();
        readFile();
        stop = System.currentTimeMillis();
        System.out.println("Reading+Filling takes " + (stop - start) + " ms");
        
        System.out.println("Start verifying...");
        start = System.currentTimeMillis();
        verify();
        stop = System.currentTimeMillis();
        System.out.println("Verifying takes " + (stop - start) + " ms");
    }

    private void writeToFile() {
        try {
            sema.acquire();
            File target = new File("/home/achr/HashMap.dat");
            if (!target.exists()) {
                target.createNewFile();
            }

            Path path = FileSystems.getDefault().getPath("/home/achr", "HashMap.dat");
            afc = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);

            // single buffer for map size
            ByteBuffer bufferMapSize = ByteBuffer.allocate(8);
            bufferMapSize.putLong(map1.size());
            bufferMapSize.flip();

            // init iterator for later use in "write()" method
            iterator = map1.keySet().iterator();

            // initial write of map size
            afc.write(bufferMapSize, afc.size(), afc, new CompletionHandler<Integer, AsynchronousFileChannel>() {

                @Override
                public void completed(Integer result, AsynchronousFileChannel attachment) {

                    // delegate writing of map entries
                    writeSet(attachment);
                }

                @Override
                public void failed(Throwable exc, AsynchronousFileChannel attachment) {
                    throw new UnsupportedOperationException("Not supported yet.");
                }
            });

            // waiting for end of write
            sema.acquireUninterruptibly();
            sema.release();
            fileSize = afc.size();
            System.out.println("File has size: " + fileSize);
            // close file
            afc.close();

        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        } catch (IOException | InterruptedException ex) {
            ex.printStackTrace();
        }
    }

    private void writeSet(AsynchronousFileChannel afc) {
        boolean somethingToWrite = false;
        boolean endOfMap = false;
        buffer.clear();

        // fill complete buffer ...
        while (buffer.hasRemaining()) {

            // but only until end of map reached
            if (iterator.hasNext()) {
                Integer key = iterator.next();
                Long value = map1.get(key);
                buffer.putInt(key);
                buffer.putLong(value);
                somethingToWrite = true;
            } else {
                endOfMap = true;
                System.out.println("done writing.");
                break;
            }
        }
        buffer.flip();
        if (somethingToWrite) {
            try {
                afc.write(buffer, afc.size(), afc, writeSetCompletion);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        if (endOfMap) {
            // signal end of write to 'main'
            sema.release();
        }
    }

    private void readFile() {
        try {
            sema.acquire();

            Path path = FileSystems.getDefault().getPath("/home/achr", "HashMap.dat");
            afc = AsynchronousFileChannel.open(path, StandardOpenOption.READ);

            final ByteBuffer mapSizeBuffer = ByteBuffer.allocate(8);
            mapSizeBuffer.clear();

            afc.read(mapSizeBuffer, 0, afc, new CompletionHandler<Integer, AsynchronousFileChannel>() {

                @Override
                public void completed(Integer result, AsynchronousFileChannel attachment) {
                    if (result != 8) {
                        throw new RuntimeException("Not able to read map size");
                    }
                    readPos += result;
                    mapSizeBuffer.flip();
                    readMapSize = mapSizeBuffer.getLong();
                    System.out.println("map has size: " + readMapSize);

                    // trigger reading set
                    buffer.clear();
                    afc.read(buffer, readPos, afc, readSetCompletion);
                }

                @Override
                public void failed(Throwable exc, AsynchronousFileChannel attachment) {
                    throw new UnsupportedOperationException("Not supported yet.");
                }
            });

            // waiting for end of read
            sema.acquireUninterruptibly();
            sema.release();
            // close file
            afc.close();

        } catch (InterruptedException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }

    }

    private void readSet(AsynchronousFileChannel afc) {
        buffer.flip();
        // get data from buffer
        for (int i = 0; i < blockFactor; i++) {
            int key = buffer.getInt();
            long val = buffer.getLong();
            map2.put(key, val);
//            System.out.println("Read: "+key+"/"+val);
        }

        if (readPos >= fileSize) {
            // signal end of read to 'main'
            sema.release();
        } else {
            // end not yet reached
            // delegate next read
            buffer.clear();
            afc.read(buffer, readPos, afc, readSetCompletion);
        }

    }
    
    private void verify() {
        boolean allOk = true;
        for(int i=0;i<count;i++) {
            Long value = map2.get(i);
            if (value==null){
                allOk = false;
                System.out.println("Key '"+i+"' is missing.");
            } else if (value.intValue()!=i) {
                allOk = false;
                System.out.println("Value for key '"+i+"' is wrong: "+value);
                break;
            }
        }
        if (!allOk) {
            System.out.println("Verification failed!");
        } else {
            System.out.println("Verification succeded!");
        }
    }

    public static void main(String[] args) {
        new Java7FileAioTest();
    }

}
```

Output auf meiner Kiste:

* Intel i7 860 4x2,8Ghz mit 8GB RAM
* Debian 6 AMD 64
* Oracle JDK7
* TB Samsung Platte SATA2, ext3 formatiert
* TestApp gestartet in NetBeans 7.1
* Nebenher noch tausend andere Dinge offen


```
Creating map: 19ms
Adding key: 0
Adding key: 1000000
Adding key: 2000000
Adding key: 3000000
Adding key: 4000000
Adding key: 5000000
Adding key: 6000000
Adding key: 7000000
Adding key: 8000000
Adding key: 9000000
Filling map takes 6196ms
Start writing...
done writing.
File has size: 120000008
Writing takes 609 ms
Write-Speed: 197.04434811165845 mb/sec
Start reading + filling map...
map has size: 10000000
Reading+Filling takes 5596 ms
Start verifying...
Verification succeded!
Verifying takes 253 ms
```

Ist mir selbst noch ein Rätsel wieso das so schnell gespeichert werden kann... Wenn jemand eine Erklärung hat: Her damit ...

Wer's selbst testen will und ggf. nicht genug Speicher hat: Einfach die "count" Variable ein wenig runterschrauben...

- Alex


----------



## maki (3. Feb 2012)

Man testet meist nur den Cache des OS anstatt den echten Durchsatz zur Platte


----------



## tuxedo (3. Feb 2012)

War/Ist ja auch meine Vermutung. 

Aber das schmälert den Test nicht. Interessant ist nach wie vor dass man mit Java den Durchsatz hinbekommt. 
Richtig interessant wird's jetzt mit einer schnellen SSD ...


----------



## tfa (3. Feb 2012)

maki hat gesagt.:


> Man testet meist nur den Cache des OS anstatt den echten Durchsatz zur Platte



Das wird's sein. Das OS cacht, die Festplatte eventuell auch.
Wenn ich in meinem frisch gestarteten Eclipse eine Volltextsuche über den ganzen Workspace mache, dauert das ca. 30 Sekunden. Die gleiche Suche danach nochmal gestartet dauert nur noch knapp 3 Sekunden. Cache ist schon was feines.


----------



## HoaX (3. Feb 2012)

jup, das mit den Caches kann man schön unter Linux sehen wenn man z.B. "hdparm -tT /dev/sda" aufruft. Kenn den Effekt auch wenn ich Filme auf USB-Stick kopiere, die ersten ~300mb flutschen, dann stoppt es.
Falls du Linux benutzt kannst du ja mal direkt nach dem Schreiben das Programm "sync" ausführen lassen und die Zeit stoppen. sync sorgt für das leeren aller Schreibpuffer und kehrt erst dann zurück, wenn dies beendet ist.


----------



## tuxedo (3. Feb 2012)

Also bei den aktuellen 120MB die da geschrieben werden lässt sich mit "sync" nix rausmessen. Geht zu schnell. Müssten dann wohl schon mehr Daten sein, damit das, wie bei dir bei 300MB, anfängt zu stocken. 

Windows wäre jetzt auch noch interessant zu sehen.


----------



## TheDarkRose (3. Feb 2012)

300MB sind auch noch wenig um wirklich die Festplatte zu testen. Müsstest schon wirklich damit anfangen ein paar GB zu schreiben. Oder du deaktivierst den Cache in den Mount Optionen.


----------



## tuxedo (3. Feb 2012)

Es ging ja nicht ums Platte testen. Es ging drum: Wieviel Durchsatz bekomm ich mit AIO und einer "normalen Platte" hin. Und so wie's ausschaut sind das aktuell um die 200MB/Sek. Mit normalen IO ist das sicherlich weniger bzw. braucht mehr Ressources (CPU, ...). Mit AIO ist das eben ein klacks.


----------



## njans (3. Feb 2012)

```
Creating map: 68ms
Adding key: 0
Adding key: 1000000
Adding key: 2000000
Adding key: 3000000
Adding key: 4000000
Adding key: 5000000
Adding key: 6000000
Adding key: 7000000
Adding key: 8000000
Adding key: 9000000
Filling map takes 4932ms
Start writing...
done writing.
File has size: 120000008
Writing takes 2232 ms
Write-Speed: 53.76344444444444 mb/sec
Start reading + filling map...
map has size: 10000000
Reading+Filling takes 3669 ms
Start verifying...
Verification succeded!
Verifying takes 2437 ms
```

bei:
* Intel Q6600 4x2,4Ghz mit 3GB RAM
* Win 7 x86 Premium
* Oracle JDK7
* Samsung HD321KJ Sata
* TestApp gestartet in Eclipse 3.7.1
* Nebenher noch tausend andere Dinge offen (und ein Download)


----------



## tuxedo (3. Feb 2012)

Interessant... Hab daheim auch einen Q6600 ... Muss da mal mit Debian 6 AMD64 gegentesten...


----------



## Marco01_809 (3. Feb 2012)

Windows auf eine Crucial m4 64GB ~320 MB/s, auf eine WD Black 1TB ebenfalls 

Beide via SATA3 angeschlossen.
i5-2500K auf einem ASUS P8Z68-V PRO mit 4x4GB DDR3-1333
Windows 7 HP x64 SP1
Oracle JDK 7 Update 2
Gestartet in Eclipse 3.7.1

EDIT: Konsolenausgabe bei der SSD:


> Creating map: 13ms
> Adding key: 0
> Adding key: 1000000
> Adding key: 2000000
> ...


----------



## schalentier (3. Feb 2012)

```
Creating map: 29ms
Adding key: 0
Adding key: 1000000
Adding key: 2000000
Adding key: 3000000
Adding key: 4000000
Adding key: 5000000
Adding key: 6000000
Adding key: 7000000
Adding key: 8000000
Adding key: 9000000
Filling map takes 18263ms
Start writing...
done writing.
File has size: 120000008
Writing takes 745 ms
Write-Speed: 161.07383624161073 mb/sec
Start reading + filling map...
map has size: 10000000
Reading+Filling takes 12262 ms
Start verifying...
Verification succeded!
Verifying takes 1357 ms
```
bei:
* Intel Q9400 4x2,66Ghz mit 4GB RAM
* Win7 64 Home Premium
* Oracle JDK7, Update 2
* Samsung HD103SJ Sata
* gestartet in IntelliJ 11
* Nebenher noch mehrere tausend andere Dinge offen (und SW:TOR im Hintergrund)

Witzigerweise hoert man, wie die Platte aus dem Cache beschrieben wird, nachdem das Programm fertig ist - auch wenn die Datei bereits geloescht wurde ;-)


----------



## TheDarkRose (3. Feb 2012)

tuxedo hat gesagt.:


> Es ging ja nicht ums Platte testen. Es ging drum: Wieviel Durchsatz bekomm ich mit AIO und einer "normalen Platte" hin. Und so wie's ausschaut sind das aktuell um die 200MB/Sek. Mit normalen IO ist das sicherlich weniger bzw. braucht mehr Ressources (CPU, ...). Mit AIO ist das eben ein klacks.



Aber nur in den Cache. Trotzdem kannst du nicht sagen, wie sich AIO wirklich beim schreiben auf die Platte verhält.


----------



## tuxedo (3. Feb 2012)

TheDarkRose hat gesagt.:


> Aber nur in den Cache. Trotzdem kannst du nicht sagen, wie sich AIO wirklich beim schreiben auf die Platte verhält.



Ist das nciht meist egal? Wenn ich mit Java Daten schreiben muss/will, und Java meldet mir "Daten fertig geschrieben", dann kann ich sie mit Java auch wieder lesen. Mag sein dass das lesen dann etwas dauert wenn er mit schreiben noch nicht fertig ist. Aber alles in allem beschleunigt es doch meine Java Anwendung. 
Aber gut, es mag konstellationen geben wo der Cache mehr stört wie nutzt... Mir ist so eine Konstellation allerdings noch nicht über den Weg gelaufen. Kommt vielleicht noch.

- Alex


----------



## TheDarkRose (3. Feb 2012)

Jo, dann hab ich wohl deinen Eingangspost etwas falsch intepretiert 


Festplattencache bzw. der Cache vom Betriebssystem selbst ist ein echter Spielverderber in einen RAID falls der Strom ausfallen sollte. Das eigentlich der einzige Fall wo der normale Cache stört.


----------



## LoR (3. Feb 2012)

Ich verstehe die ganze Aufregung darüber nicht. Außer das das Teil (AsynchronousFileChannel) jetzt zusätzlich einen CompletionHandler implementiert und noch ein paar kleinere (minimale) Features enthält gibts diese Funktionalität schon seit Java 5 (vgl. FileChannel). Wirklich interessant wirds eigeintlich nur in Bezug auf Sockets. Die können jetzt wesentlich leichter implementiert werden. Das ist aber auch schon alles.


----------



## tuxedo (3. Feb 2012)

Naja, nicht ganz. Bei NIO (reiner FileChannel) hast du selbst in einem Thread die Daten in die File schaufeln müssen. Bei AIO "schubst" du das nur an, und dann läufts von alleine (wenn du die CompletionHandler richtig miteinander verknüpft hast). Wenn ich die Semaphore nicht drin hätte, die wartet bis alles fertig ist, würde die Anwendung sofort terminieren.

Ein weiterer Vorteil von AIO: Da nicht aktiv von Java aus in die File geschrieben wird, kann ich mit meiner Anwendung auch keinen Puffer überlasten. Wird der überlastet, so wird der Java-Thread, der die Daten reinschreibt gebremst. Läuft ja alles synchron. Mit AIO fällt diese Bremsmöglichkeit meiner Anwendung weg. Und da es keinen Java-Thread braucht der die Daten schreibt, fallen auch (je nach File und Blockgröße) verdammt viele Thread-Kontext-Wechsel weg... Ergo: Mehr Zeit für's eigentliche...

- Alex


----------



## Kr0e (3. Feb 2012)

Ich sag doch AIO ist geil  Neues kann auch mal gut sein 

Ab JDK7 sind die ByteBuffer enorm verbessert worden. Find zwar grad nicht mehr die Quelle, aber kam direkt von Oracle. Man sollte nun stets direkte Buffer verwenden, da die interne Handhabung deutlich verbessert wurde. Somit könnte man ggf. sogar noch mehr rauskitzeln...


----------



## Empire Phoenix (3. Feb 2012)

Bis auf das gc davon.. die is imernoch s******e

der cleart die directmemorys nur wenn der heap leerläuft.
alsow enn man meherer hundert megagbyte driect memory durchpumpt hat man ganz schnell mal ne exception dadurch.


----------



## Kr0e (3. Feb 2012)

Empire Phoenix hat gesagt.:


> der cleart die directmemorys nur wenn der heap leerläuft.
> alsow enn man meherer hundert megagbyte driect memory durchpumpt hat man ganz schnell mal ne exception dadurch.



Ist mir bisher so nicht aufgefallen. Werds mal testen.


----------



## Cola_Colin (3. Feb 2012)

```
Creating map: 29ms
Adding key: 0
Adding key: 1000000
Adding key: 2000000
Adding key: 3000000
Adding key: 4000000
Adding key: 5000000
Adding key: 6000000
Adding key: 7000000
Adding key: 8000000
Adding key: 9000000
Filling map takes 12099ms
Start writing...
done writing.
File has size: 120000008
Writing takes 711 ms
Write-Speed: 168.77638255977496 mb/sec
Start reading + filling map...
map has size: 10000000
Reading+Filling takes 5673 ms
Start verifying...
Verification succeded!
Verifying takes 2923 ms
```

C2D E8400
4GB DDR2 800
Win7 Pro x64
Crucial m4 SSD 128GB

tjo, der Cache einer SSD ist wohl nicht schneller als der einer HDD


----------



## maki (3. Feb 2012)

> tjo, der Cache einer SSD ist wohl nicht schneller als der einer HDD


Vor allem hängen der Cache des Laufwerks hinter dem Cache des OS, und letzteres kann u.U. etwas inteligenter Cachen als ein Laufwerk welches nicht viel von Dateisystemen oder gar Dateien versteht 

Bei mir gibt es immense Schwankungen, je nachdem wie oft ich das laufen lasse.

zB. beim ersten mal:

```
Creating map: 49ms
Adding key: 0
Adding key: 1000000
Adding key: 2000000
Adding key: 3000000
Adding key: 4000000
Adding key: 5000000
Adding key: 6000000
Adding key: 7000000
Adding key: 8000000
Adding key: 9000000
Filling map takes 13254ms
Start writing...
done writing.
File has size: 120000008
Writing takes 715 ms
Write-Speed: 167.83217902097903 mb/sec
Start reading + filling map...
map has size: 10000000
Reading+Filling takes 11545 ms
Start verifying...
Verification succeded!
Verifying takes 3577 ms
```

ein paar male später dann (inkl. spielen mit Xms, Xmx und server Parameter)

```
Creating map: 48ms
Adding key: 0
Adding key: 1000000
Adding key: 2000000
Adding key: 3000000
Adding key: 4000000
Adding key: 5000000
Adding key: 6000000
Adding key: 7000000
Adding key: 8000000
Adding key: 9000000
Filling map takes 13165ms
Start writing...
done writing.
File has size: 840000056
Writing takes 752 ms
Write-Speed: 1117.0213510638298 mb/sec
Start reading + filling map...
map has size: 10000000
Exception in thread "Thread-1" java.nio.BufferUnderflowException
	at java.nio.Buffer.nextGetIndex(Buffer.java:498)
	at java.nio.HeapByteBuffer.getInt(HeapByteBuffer.java:355)
	at Java7FileAioTest.readSet(Java7FileAioTest.java:243)
	at Java7FileAioTest.access$1(Java7FileAioTest.java:239)
	at Java7FileAioTest$2.completed(Java7FileAioTest.java:51)
	at Java7FileAioTest$2.completed(Java7FileAioTest.java:1)
	at sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:126)
	at sun.nio.ch.SimpleAsynchronousFileChannelImpl$2.run(SimpleAsynchronousFileChannelImpl.java:336)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
	at java.lang.Thread.run(Thread.java:722)
```
k.A. woher die Exception kommt, könnte an meinem Setup liegen ???:L

"provisorisches" Ubuntu 10.04 32 Bit (läuft im Moment recht instabil, ist soz. mein "Not-OS") auf einem "alten" Quadcore 2,66GHz oops: weiss grad nicht genau...) mit irgendeiner alten & langsamen (5400 U/min, SATA Platte, 500 GB) Festplatte, Java 1.7.0 (build 1.7.0-b147).


----------



## LoR (4. Feb 2012)

tuxedo hat gesagt.:


> Naja, nicht ganz. Bei NIO (reiner FileChannel) hast du selbst in einem Thread die Daten in die File schaufeln müssen. Bei AIO "schubst" du das nur an, und dann läufts von alleine (wenn du die CompletionHandler richtig miteinander verknüpft hast). Wenn ich die Semaphore nicht drin hätte, die wartet bis alles fertig ist, würde die Anwendung sofort terminieren.
> 
> Ein weiterer Vorteil von AIO: Da nicht aktiv von Java aus in die File geschrieben wird, kann ich mit meiner Anwendung auch keinen Puffer überlasten. Wird der überlastet, so wird der Java-Thread, der die Daten reinschreibt gebremst. Läuft ja alles synchron. Mit AIO fällt diese Bremsmöglichkeit meiner Anwendung weg. Und da es keinen Java-Thread braucht der die Daten schreibt, fallen auch (je nach File und Blockgröße) verdammt viele Thread-Kontext-Wechsel weg... Ergo: Mehr Zeit für's eigentliche...
> 
> - Alex



Es braucht schon einen Thread. Der wird jetzt nur automatisch aus dem internen Threadpool genommen/erzeugt, wenn du keinen ExecutorService angibst. Wie gesagt die Funktionalität ist bereits lange enthalten, nur das du vorher den Thread selbst erzeugen und evtl einen eigenen Listener installieren musstest. Die Unterscheide sind aus meiner Sicht aber nicht besonders gross.


----------



## Kr0e (4. Feb 2012)

Nein, der Witz ist, dass der interne ThreadPool nur für die Ausführung des Listeners verantwortlich ist.
Das Lesen der Daten geschieht irgendwo tief im OS. Das wird auch gar nicht spezifiziert. Dennoch ist es möglich dass dies dort direkt geschieht. Wie Tuxedo bereits sagte, spart man sich da Thread-Kontextwechsel, die ziemlich teuer werden können, wenn man es häufig macht.


Hier werden viele Vorteile des neuen Konzepts im Zusammenhang mit dem Grizzly Framework näher beleuchtet: Parleys.com - The Next Generation E-Learning Platform

NIO1.4 mit den Selectors verkompliziert das wirklich nur. AIO macht genau das, was man in so einem Fall immer machen sollte, es überlässt dem OS die volle Kontrolle über das Lesen/Schreiben der Daten.

Unter Linux könnte es sogar noch aus anderen Gründen schneller sein. Laut der Doc wird bei AIO unter Linux epoll genutzt und bei NIO1.4 nur poll(). Zumindest in Jdk6, soweit ich mich errinnere. epoll ist in Etwa das Linux eigene AIO Konzept. Unter Windows wird AIO mit Overlapped I0 bzw. Completions Ports implementiert.

Alles in Allem ist es wirklcih Zeit, NIO1.4 aufzugeben. Selbst wenn AIO minimal langsamer wäre, würde ich AIO ab jetzt benutzen. Die Handhabung ist einfach super einfach im Gegensatz zu NIO1.4.

Gruß,
Chris


----------



## GUI-Programmer (4. Feb 2012)

Also bei mir funktioniert es einfach nicht.

Mein System:
Betriebssystem: Windows 7 HomePremium 64 bit
Prozessor: Intel Core i5 650 @3.20 GHz
Arbeitsspeicher: 4 GB Ram
Grafikkarte: ATI Radeon HD 5700 Series

Ausgabe bei Start mit Java 1.7.0 32Bit:

```
Creating map: 37ms
Adding key: 0
Adding key: 1000000
Adding key: 2000000
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.HashMap.addEntry(Unknown Source)
	at java.util.HashMap.put(Unknown Source)
	at Java7FileAioTest.<init>(Java7FileAioTest.java:77)
	at Java7FileAioTest.main(Java7FileAioTest.java:282)
```

Ausgabe bei Start mit Java 1.7.0 65Bit:

```
Creating map: 19ms
Adding key: 0
Adding key: 1000000
Adding key: 2000000
Adding key: 3000000
Adding key: 4000000
Adding key: 5000000
Adding key: 6000000
Adding key: 7000000
Adding key: 8000000
Adding key: 9000000
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
```

Ach ja: Der Java Compiler ist 32 Bit (1.7.0).

Und unter Ecplise wurde komischerweise garnicht erst richtig kompiliert:

```
Exception in thread "main" java.lang.Error: Unresolved compilation problems: 
	Incorrect number of arguments for type HashMap<K,V>; it cannot be parameterized with arguments <?>
	Syntax error on token "<", ? expected after this token
	Incorrect number of arguments for type HashMap<K,V>; it cannot be parameterized with arguments <?>
	Syntax error on token "<", ? expected after this token
	IOException.InterruptedException cannot be resolved to a type
	Syntax error on token "|", . expected

	at Java7FileAioTest.<init>(Java7FileAioTest.java:64)
	at Java7FileAioTest.main(Java7FileAioTest.java:282)
```


----------



## Kr0e (4. Feb 2012)

Du brauchst die Codebasis 1.7. Sonst meckert der Compiler, da der Diamond Operator unbekannt ist.
Allerdings hat das nichts mit dem Out of Memory zu tun...


----------



## GUI-Programmer (4. Feb 2012)

Kr0e hat gesagt.:


> Du brauchst die Codebasis 1.7. Sonst meckert der Compiler, da der Diamond Operator unbekannt ist.
> Allerdings hat das nichts mit dem Out of Memory zu tun...



Und wie mach ich das für Eclipse? Denn schließlich hat das Kompilieren per Konsole ja ohne Fehler geklappt.


----------



## maki (4. Feb 2012)

Die Exception bei mir lag daran, dass ich die Datei HashMap.dat nicht gelöscht hatte zwischen den läufen


----------



## Kr0e (4. Feb 2012)

Project properties -> Java Compiler -> JDK - Compliance -> 1.7


----------



## GUI-Programmer (4. Feb 2012)

Kr0e hat gesagt.:


> Project properties -> Java Compiler -> JDK - Compliance -> 1.7



Das hab ich mir auch schon gedacht, aber da gibts nur 1.4, 1.5 und 1.6 , wobei 4 und 5 niemals auf meinen Pc war und Java6 auch schon lange deinstalliert wurde. Also was muss ich tun, damit es geht? Evtl. gar Java nochmal deinstallieren und erneut installieren?


----------



## tfa (4. Feb 2012)

Du brauchst eine aktuelle Eclipse-Version (3.7 Indigo).
Eclipse bringt seinen eigenen Compiler mit. Zusätzlich installieren musst du da nichts.


----------



## GUI-Programmer (4. Feb 2012)

tfa hat gesagt.:


> Du brauchst eine aktuelle Eclipse-Version (3.7 Indigo).
> Eclipse bringt seinen eigenen Compiler mit. Zusätzlich installieren musst du da nichts.



Hab ich eigentlich auch. Zitat Eclipse:





> Eclipse SDK
> 
> Version: 3.7.0
> Build id: I20110613-1736
> ...


----------



## Kr0e (4. Feb 2012)

Ich meine das ist erst ab 3.7.1... Eclipse hat ne weile gechillt, im Gegensatz zu Netbeans. Aber inzwischen sollte vollständiger Support da sein.


----------



## tfa (4. Feb 2012)

Das ist die erste Indigo-Version. Du brauchst die 3.7 SR1. 

Eclipse Announces Full Support for Java 7


----------



## GUI-Programmer (4. Feb 2012)

tfa hat gesagt.:


> Das ist die erste Indigo-Version. Du brauchst die 3.7 SR1.
> 
> Eclipse Announces Full Support for Java 7



OK, Danke. Dann muss ich mir also nur noch unter Eclipse IDE for Java Developers die 64 Bit Version runterladen, richtig? Oder spreche irgendwas gegen die 32 Bit Version (außer dann üblichen Vorteilen einer 64Bit Version) ? Denn bei mir liegt die Downloadgeschwindigkeit momentan noch bei ca. 80 kB/s. Daher würde ich lieber nur 1x runterladen, damit ich diese auch für mein Netbook mit Windows 7 Starter nutzen kann.


----------



## tfa (4. Feb 2012)

32 oder 64Bit ist egal.


----------



## GUI-Programmer (5. Feb 2012)

OK, danke schon mal. Die Installation hat ohne Problem geklappt und der Compiler wurde umgestellt. Auserdem hab ich auch die Pfade für die "HashMap.dat" - Datei geändert:
[JAVA=110]File target = new File("C:/Users/MeinBenutzername/Desktop/HashMap.dat");[/code]
[JAVA=115]Path path = FileSystems.getDefault().getPath("C:/Users/MeinBenutzername/Desktop", "HashMap.dat");[/code]
Doch die Fehlermeldungen bleiben nach wie vor. Habe auch mal der Variable count den Wert 500000 verpasst, ändert aber nichts. Wo könnte der Fehler/das Problem also liegen?


----------



## tuxedo (6. Feb 2012)

LoR hat gesagt.:


> Ich verstehe die ganze Aufregung darüber nicht. Außer das das Teil (AsynchronousFileChannel) jetzt zusätzlich einen CompletionHandler implementiert und noch ein paar kleinere (minimale) Features enthält gibts diese Funktionalität schon seit Java 5 (vgl. FileChannel). Wirklich interessant wirds eigeintlich nur in Bezug auf Sockets. Die können jetzt wesentlich leichter implementiert werden. Das ist aber auch schon alles.





Kr0e hat gesagt.:


> Nein, der Witz ist, dass der interne ThreadPool nur für die Ausführung des Listeners verantwortlich ist.
> Das Lesen der Daten geschieht irgendwo tief im OS. Das wird auch gar nicht spezifiziert. Dennoch ist es möglich dass dies dort direkt geschieht. Wie Tuxedo bereits sagte, spart man sich da Thread-Kontextwechsel, die ziemlich teuer werden können, wenn man es häufig macht.



Hab mal beim reinen lesen NIO mit AIO verglichen.

Hab in meinem AIO Sample beim lesen einfach das "put" für das füllen der Map weggelassen.
Lesezeit für die 120MB Daten: 53ms

Gleiches Beispiel mit NIO (aber mit Java7):


```
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 *
 * @author achr
 */
public class NioRead {

    public static void main(String[] args) throws FileNotFoundException, IOException {

        int setSize = 12;
        int blockFactor = 1000000;
        ByteBuffer buffer = ByteBuffer.allocate(blockFactor * setSize); 

        long start = System.currentTimeMillis();
        File f = new File("/home/achr/HashMap.dat");
        RandomAccessFile ram = new RandomAccessFile(f, "r");
        FileChannel channel = ram.getChannel();
        ByteBuffer sizeBuffer = ByteBuffer.allocate(8);
        channel.read(sizeBuffer);
        sizeBuffer.flip();
        long size = sizeBuffer.getLong();

        for (int i = 0; i < 10; i++) {
            channel.read(buffer);
            buffer.flip();
            for (int ii = 0; ii < blockFactor; ii++) {
                int key = buffer.getInt();
                long val = buffer.getLong();
//                System.out.println(key+" -> "+val);
            }
        }
        long stop = System.currentTimeMillis();
        System.out.println("Time taken to read: " + (stop - start) + "ms");
    }
}
```

Dauer: ca. 75ms. 

Beim blockweisen lesen ist AIO ein klein wenig im Vorteil was die Geschwindigekti angeht. NIO ist hier nicht nur langsamer (ein klein wenig), sondern auch, ressourcenhungriger. 

Ich denke wenn man nicht in großen Blöcken liest ist der Unterschied der geschwindigkeit zwischen AIO und NIO noch kleiner. Müsste man mal testen.

[EDIT]Hmm, okay. Meine Vermutung stimmt nicht ganz. 

Seltsamerweise ist AIO beim lesen von 12byte großen Blöcken schneller:

12byte blöcke: 9ms
12000000byte blöcken: 53ms.

Vergleich zu NIO:

12byte blöcke: 1702ms
12000000byte blöcke: 75ms

So oder so: AIO ist überlegen.
[/EDIT]


----------



## tuxedo (6. Feb 2012)

GUI-Programmer hat gesagt.:


> Doch die Fehlermeldungen bleiben nach wie vor. Habe auch mal der Variable count den Wert 500000 verpasst, ändert aber nichts. Wo könnte der Fehler/das Problem also liegen?



Du musst in Eclipse einstellen welche Java-Version du für das Projekt benutzt. Daneben musst du noch das JDK7 installiert haben, sowie Eclipse beigebracht haben, dieses JDK7 auch zu benutzen (geht in den Preferences bei den JREs).

Oder du benutzt halt Netbeans ;-)


----------



## Kr0e (6. Feb 2012)

Aber auch wenn AIO minimalst langsamer wäre... Ich mag einfach die einfache Handhabung im Gegensatz zu NIO. Aber da es schneller ist, umso besser


----------



## tuxedo (6. Feb 2012)

Naja, beim File-IO gibt sich's nicht viel. Finde beides da sehr einfach. NIO ist nur ein klein wenig "einfacher/logischer". Mit AIO muss ich immer nochmal drüber nachdenken wierum das jetzt gehört: Die indirektion durch den CompletionHandler macht mir hin und wieder einen Knoten in die Hirnwindungen. Bin vielleicht aber auch schon zu viel mit normalem IO unterwegs gewesen. Nach weiteren 1-2 Tagen AIO wird das aber wohl verschwinden.

Beim Netzwerk-IO geb ich dir allerdings vorbehaltlos recht. Da ist NIO echt kompliziert und AIO eine echte Wohltat.


----------



## GUI-Programmer (6. Feb 2012)

Hat vielleicht noch irgendjemand eine Idee, warum es bei mir nicht geht? Die Systemdaten hab ich ja bereits mitgeteilt. Ich hab auch schon versucht irgendwas über den Cache meiner Festplatte zu erfahren, bin aber gescheitert. Also wenn irgendjemand noch einen Vorschlag hat, dann her damit. Denn schließlich geht das Programm ja bei allen anderen.


----------



## bERt0r (6. Feb 2012)

Naja, eure Wahnsinnszeiten liegen aber wohl auch an euren Hightech computern. Auf meinem 4 Jahre alten Laptop hat man einen viel "normaleren" Datendurchsatz ;D
	
	
	
	





```
Creating map: 86ms
Adding key: 0
Adding key: 1000000
Adding key: 2000000
Adding key: 3000000
Adding key: 4000000
Adding key: 5000000
Adding key: 6000000
Adding key: 7000000
Adding key: 8000000
Adding key: 9000000
Filling map takes 13808ms
Start writing...
done writing.
File has size: 120000008
Writing takes 4993 ms
Write-Speed: 24.033648708191464 mb/sec
Start reading + filling map...
map has size: 10000000
Reading+Filling takes 6318 ms
Start verifying...
Verification succeded!
Verifying takes 2653 ms
```


----------



## tuxedo (7. Feb 2012)

Hast du evtl noch Virenscanner und Co. laufen? Die bremsen für gewöhnlich den Transfer mächtig ab.
Evtl. hast du auch ne langsame, energieparende Festplatte mit 5400RPM und wenig bis kein Cache oder sowas. 

Weil mein zweiter Testrechner hier ist auch 4 Jahre alt, aber wurde "damals" mit guter Hardware bestückt...Und da läufts auch super schnell.


----------



## GUI-Programmer (8. Feb 2012)

Irgendjemand noch ne Idee???

Wie bereits gesagt: Compiler und Virtuelle Maschine passen, und auch der Pfad, aber dennoch die beschriebene Fehlermeldung, sowohl wenn ich es per Eclipse kompiliere und ausführe als auch wenn ich es per Konsole kompiliere und ausführe.


----------



## tfa (8. Feb 2012)

Java-Compliance hast du auch auf 1.7 gestellt? Zeig nochmal die Fehlermeldung.


----------



## GUI-Programmer (8. Feb 2012)

Hier nochmal alles komplett in 4 Bilder ausgedrückt.


----------



## tuxedo (8. Feb 2012)

> Wer's selbst testen will und ggf. nicht genug Speicher hat: Einfach die "count" Variable ein wenig runterschrauben...



Wieso schreib ich eigentlich dazu dass man den "count" ggf. runtersetzen soll um den Heap nicht zu fluten wenn's keiner liest?!? 

Denn gnau das passiert bei dir... 10Mio Einträge scheinen bei dir nicht so ohne weiteres in den Speicher zu passen.. :autsch:

Manchmal hilft's wenn man 1 und 1 zusammen zählt.

--> RTFM :rtfm:


----------



## tfa (8. Feb 2012)

GUI-Programmer hat gesagt.:


> Hier nochmal alles komplett in 4 Bilder ausgedrückt.



Tut mir leid, Dropbox kann ich hier nicht aufrufen.


----------



## tuxedo (8. Feb 2012)

@tfa

Brauchst du auch nicht. Ist ein klassicher "OutOfMemory" Fehler. Hab mal exemplarisch eins der 4 Bilder angehängt.


----------



## GUI-Programmer (8. Feb 2012)

OK, der Fehler lag bei der Person vor der Tastatur!

Hab es nun hinbekommen mit 9 000 000 Einträgen (count). Aber mal was anderes:
1. Warum muss man nach jeden Durchlauf die HashMap.dat wieder löschen, da ansonsten diese Exception hier fliegt:

```
Creating map: 43ms
Adding key: 0
Adding key: 1000000
Adding key: 2000000
Adding key: 3000000
Adding key: 4000000
Adding key: 5000000
Adding key: 6000000
Adding key: 7000000
Adding key: 8000000
Filling map takes 10701ms
Start writing...
done writing.
File has size: 216000016
Writing takes 811 ms
Write-Speed: 266.3378742293465 mb/sec
Start reading + filling map...
map has size: 9000000
Exception in thread "Thread-4" java.nio.BufferUnderflowException
	at java.nio.Buffer.nextGetIndex(Unknown Source)
	at java.nio.HeapByteBuffer.getLong(Unknown Source)
	at Java7FileAioTest.readSet(Java7FileAioTest.java:244)
	at Java7FileAioTest.access$1(Java7FileAioTest.java:239)
	at Java7FileAioTest$2.completed(Java7FileAioTest.java:51)
	at Java7FileAioTest$2.completed(Java7FileAioTest.java:1)
	at sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at sun.nio.ch.WindowsAsynchronousFileChannelImpl$ReadTask.completed(Unknown Source)
	at sun.nio.ch.Iocp$EventHandlerTask.run(Unknown Source)
	at sun.nio.ch.AsynchronousChannelGroupImpl$1.run(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
```

2. Warum liegt bei mir die beste Schreibgeschwindigkeit um die 208 Mb/s bei etwa 7 000 000 Einträgen? Weniger und mehr Einträge geben eine schlechtere. Inwiefern hängt das mit den Cache zusammen?

3. Wow!!! Eine Schreibgeschwindigkeit von 208 Mb/s!!! CrystelDiskMark und normales Kopieren mit den Windows-Explorer erreichen in etwa 150 Mb/s!!!


----------



## tuxedo (8. Feb 2012)

> 1. Warum muss man nach jeden Durchlauf die HashMap.dat wieder löschen, da ansonsten diese Exception hier fliegt:



Wirf doch mal zur Abwechslung einen Blick in den Code... 

Aber gut, weil ich so guter Dinge bin verrat ich's dir: Beim zweiten Start werden die Daten einfach angehängt, aber das programm geht davon aus dass dem nicht so ist. Ergo passt die Implementierung nicht mehr zur Datei. Deshalb der Fehler. Ich war bis dato einfach zu faul das zu ändern...



> 2. Warum liegt bei mir die beste Schreibgeschwindigkeit um die 208 Mb/s bei etwa 7 000 000 Einträgen? Weniger und mehr Einträge geben eine schlechtere. Inwiefern hängt das mit den Cache zusammen?



Kann ich dir nicht sagen. Hab Windows nicht implementiert. Linux übrigens auch nicht ;-)
Was hälst du davon wenn du versuchst es raus zu finden?



> 3. Wow!!! Eine Schreibgeschwindigkeit von 208 Mb/s!!! CrystelDiskMark und normales Kopieren mit den Windows-Explorer erreichen in etwa 150 Mb/s!!!



Du kannst auch Äpfel mit Birnen vergleichen: "Wow, Äpfel sind viel runder als Birnen."

Mein Test schreibt 1Mio Einträge á 12bytes pro Schreibaktion. macht rund 1,2MB Blockgröße. Evtl. benutzt Windows und dein Benchmark eine andere Blockgröße?
Und wie hier schon erwähnt wurde: Je mehr man kopiert (insgesamt) desto eher stößt man an die Grenze des Caches. Hat man die Grenze erreicht, bricht die Geschwindigkeit ein. Mit 9Mio Einträgen in der Map bist du bei nur 108MB. Hier wurde aber von Cache-Grenzen bei 300MB berichtet... Womit wir wieder beim Thema Äpfel und Birnen wären.. 
*
Und um's nochmal klarzustellen*:

Dieser Test sollte nur zeigen dass Java überhaupt solch einen "hohen" Durchsatz (mit Hilfe von AIO) schafft. Das ist kein Festplattenbenchmark oder ähnliches....

- Alex


----------



## GUI-Programmer (8. Feb 2012)

tuxedo hat gesagt.:


> Beim zweiten Start werden die Daten einfach angehängt, aber das programm geht davon aus dass dem nicht so ist.



Also einfach vorher das FileSystem überprüfen ob die Datei bereits existerst und wenn ja, dann löschen. Oder gibts einen besseren Vorschlag, sodass die Datei nicht gelöscht und erstellt werden muss?



tuxedo hat gesagt.:


> Mein Test schreibt 1Mio Einträge á 12bytes pro Schreibaktion. macht rund 1,2MB Blockgröße. Evtl. benutzt Windows und dein Benchmark eine andere Blockgröße?



OK, dann weiß ich bescheid. Werd das ganze mal nochmals auf ganz unterschiedlichen Systemen testen:
 - 5 Jahre alter PC mit Windows XP
 - Ubuntu 11.10 in meiner VirtualBox
 - Netbook Asus Eee PC R105 mit Intel Atom, 2Gb RAM und Windows 7 Starter
Melde dann irgwann das Ergebnis.


----------



## tuxedo (8. Feb 2012)

Und was will uns das Ergebnis dann sagen? Dass es auf furchtbar langsamen/alten Maschinen langsamer geht und auf super schnellen ggf. schneller? Das wissen wir mittlerweile. 

Interessant wird's erst, wenn du bei gleichen Vorrauseetzungen und Parametrisierung mit einer anderen Implementierung auf der gleichen Maschine mehr hin bekommst wie mit AIO...


----------



## GUI-Programmer (8. Feb 2012)

tuxedo hat gesagt.:


> Und was will uns das Ergebnis dann sagen? Dass es auf furchtbar langsamen/alten Maschinen langsamer geht und auf super schnellen ggf. schneller? Das wissen wir mittlerweile.
> 
> Interessant wird's erst, wenn du bei gleichen Vorrauseetzungen und Parametrisierung mit einer anderen Implementierung auf der gleichen Maschine mehr hin bekommst wie mit AIO...



OK, du hast ja eigentlich Recht. Das mit den unterschiedlichen Systemen ist wahrscheinlich nur für mich interessant. Und *mehr hin bekommen* werde ich wahrscheinlich vorerst nicht, aber interessant wärs schon.


----------



## Reality (20. Feb 2012)

Gibt es eigentlich irgendwelche Tutorials bzgl. CompletionHandler, Future, Semaphore, ByteBuffer?

Danke im Voraus.
Reality


----------



## tuxedo (20. Feb 2012)

CompletionHandler sind AFAIK neu seit Java7. Für den Rest gibt's tonnenweise Doku im Netz, da schon seit Java5 (oder früher?) verfügbar. Schau ma besten mal in die Java Insel...
Wie CompletionHandler funktionieren zeigt ja mein Sample. Mir fällt jetzt spontan auch nicht ein was es dazu noch mehr zu erklären gäbe (was nicht schon im JavaDoc steht)?! Ist ja nicht wirklich "kompliziert", man muss nur anders denken... Von Java Seite wirft man das ganze nur einmal an und übergibt einen CompletionHandler. Der wird dann von <irgendwo aus der JVM> gerufen wenn die losgetretene Aktion fertig ist. Der Trick ist, mit einem CompletionHandler die nächste Aktion auszulösen und erneut einen CompletionHandler zu übergeben. Damit geht das Spiel von vorne los. Und das eben so lang, bis man entscheidet "so, genug jetzt, keine weitere Aktion und somit auch kein CompletionHandler mehr".

- Alex


----------

