Galileo Computing :: Java 7 - Mehr als eine Insel - 3 Datenstrukturen und Algorithmen
Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Neues in Java 7
2 Threads und nebenläufige Programmierung
3 Datenstrukturen und Algorithmen
4 Raum und Zeit
5 Dateien, Verzeichnisse und Dateizugriffe
6 Datenströme
7 Die eXtensible Markup Language (XML)
8 Dateiformate
9 Grafische Oberflächen mit Swing
10 Grafikprogrammierung
11 Netzwerkprogrammierung
12 Verteilte Programmierung mit RMI
13 RESTful und SOAP Web-Services
14 JavaServer Pages und Servlets
15 Applets
16 Datenbankmanagement mit JDBC
17 Technologien für die Infrastruktur
18 Reflection und Annotationen
19 Dynamische Übersetzung und Skriptsprachen
20 Logging und Monitoring
21 Java Native Interface (JNI)
22 Sicherheitskonzepte
23 Dienstprogramme für die Java-Umgebung
Stichwort

Download:
- openbook, ca. 21,3 MB
Buch bestellen
Ihre Meinung?

Spacer
Java 7 - Mehr als eine Insel von Christian Ullenboom
Das Handbuch zu den Java SE-Bibliotheken
Buch: Java 7 - Mehr als eine Insel

Java 7 - Mehr als eine Insel
Galileo Computing
1433 S., 2012, geb.
49,90 Euro, ISBN 978-3-8362-1507-7
Pfeil 3 Datenstrukturen und Algorithmen
Pfeil 3.1 Datenstrukturen und die Collection-API
Pfeil 3.1.1 Designprinzip mit Schnittstellen, abstrakten und konkreten Klassen
Pfeil 3.1.2 Die Basis-Schnittstellen Collection und Map
Pfeil 3.1.3 Die Utility-Klassen Collections und Arrays
Pfeil 3.1.4 Das erste Programm mit Container-Klassen
Pfeil 3.1.5 Die Schnittstelle Collection und Kernkonzepte
Pfeil 3.1.6 Schnittstellen, die Collection erweitern, und Map
Pfeil 3.1.7 Konkrete Container-Klassen
Pfeil 3.1.8 Generische Datentypen in der Collection-API
Pfeil 3.1.9 Die Schnittstelle Iterable und das erweiterte for
Pfeil 3.2 Listen
Pfeil 3.2.1 Erstes Listen-Beispiel
Pfeil 3.2.2 Auswahlkriterium ArrayList oder LinkedList
Pfeil 3.2.3 Die Schnittstelle List
Pfeil 3.2.4 ArrayList
Pfeil 3.2.5 LinkedList
Pfeil 3.2.6 Der Feld-Adapter Arrays.asList()
Pfeil 3.2.7 ListIterator *
Pfeil 3.2.8 toArray() von Collection verstehen – die Gefahr einer Falle erkennen
Pfeil 3.2.9 Primitive Elemente in Datenstrukturen verwalten
Pfeil 3.3 Mengen (Sets)
Pfeil 3.3.1 Ein erstes Mengen-Beispiel
Pfeil 3.3.2 Methoden der Schnittstelle Set
Pfeil 3.3.3 HashSet
Pfeil 3.3.4 TreeSet – die sortierte Menge
Pfeil 3.3.5 Die Schnittstellen NavigableSet und SortedSet
Pfeil 3.3.6 LinkedHashSet
Pfeil 3.4 Stack (Kellerspeicher, Stapel)
Pfeil 3.4.1 Die Methoden von Stack
Pfeil 3.5 Queues (Schlangen) und Deques
Pfeil 3.5.1 Queue-Klassen
Pfeil 3.5.2 Deque-Klassen
Pfeil 3.5.3 Blockierende Queues und Prioritätswarteschlangen
Pfeil 3.5.4 PriorityQueue
Pfeil 3.6 Assoziative Speicher
Pfeil 3.6.1 Die Klassen HashMap und TreeMap
Pfeil 3.6.2 Einfügen und Abfragen der Datenstruktur
Pfeil 3.6.3 Über die Bedeutung von equals() und hashCode()
Pfeil 3.6.4 Eigene Objekte hashen
Pfeil 3.6.5 IdentityHashMap
Pfeil 3.6.6 Das Problem von veränderten Elementen
Pfeil 3.6.7 Aufzählungen und Ansichten des Assoziativspeichers
Pfeil 3.6.8 Die Arbeitsweise einer Hash-Tabelle *
Pfeil 3.7 Die Properties-Klasse
Pfeil 3.7.1 Properties setzen und lesen
Pfeil 3.7.2 Properties verketten
Pfeil 3.7.3 Hierarchische Eigenschaften
Pfeil 3.7.4 Eigenschaften auf der Konsole ausgeben *
Pfeil 3.7.5 Properties laden und speichern
Pfeil 3.7.6 Klassenbeziehungen: Properties und Hashtable *
Pfeil 3.8 Mit einem Iterator durch die Daten wandern
Pfeil 3.8.1 Die Schnittstelle Iterator
Pfeil 3.8.2 Der Iterator kann (eventuell auch) löschen
Pfeil 3.8.3 Einen Zufallszahleniterator schreiben
Pfeil 3.8.4 Iteratoren von Sammlungen, das erweiterte for und Iterable
Pfeil 3.8.5 Fail-Fast-Iterator und die ConcurrentModificationException
Pfeil 3.8.6 Die Schnittstelle Enumerator *
Pfeil 3.9 Algorithmen in Collections
Pfeil 3.9.1 Die Bedeutung von Ordnung mit Comparator und Comparable
Pfeil 3.9.2 Sortieren
Pfeil 3.9.3 Den größten und kleinsten Wert einer Collection finden
Pfeil 3.9.4 Nicht-änderbare Datenstrukturen, immutable oder nur-lesen?
Pfeil 3.9.5 Null Object Pattern und leere Sammlungen/Iteratoren zurückgeben
Pfeil 3.9.6 Mit der Halbierungssuche nach Elementen fahnden
Pfeil 3.9.7 Ersetzen, Kopieren, Füllen, Umdrehen, Rotieren *
Pfeil 3.9.8 Listen durchwürfeln *
Pfeil 3.9.9 Häufigkeit eines Elements *
Pfeil 3.9.10 Singletons *
Pfeil 3.9.11 nCopies() *
Pfeil 3.10 Spezielle thread-sichere Datenstrukturen
Pfeil 3.10.1 Wait-free-Algorithmen
Pfeil 3.10.2 Nebenläufiger Assoziativspeicher und die Schnittstelle ConcurrentMap
Pfeil 3.10.3 ConcurrentLinkedQueue
Pfeil 3.10.4 CopyOnWriteArrayList und CopyOnWriteArraySet
Pfeil 3.10.5 Wrapper zur Synchronisation
Pfeil 3.10.6 Blockierende Warteschlangen
Pfeil 3.10.7 ArrayBlockingQueue und LinkedBlockingQueue
Pfeil 3.10.8 PriorityBlockingQueue
Pfeil 3.11 Google Guava (aka Google Collections Library)
Pfeil 3.11.1 Beispiel Multi-Set und Multi-Map
Pfeil 3.11.2 Datenstrukturen aus Guava
Pfeil 3.11.3 Utility-Klassen von Guava
Pfeil 3.11.4 Prädikate
Pfeil 3.11.5 Transformationen
Pfeil 3.12 Die Klasse BitSet für Bitmengen *
Pfeil 3.12.1 Ein BitSet anlegen, füllen und erfragen
Pfeil 3.12.2 Mengenorientierte Operationen
Pfeil 3.12.3 Methodenübersicht
Pfeil 3.12.4 Primzahlen in einem BitSet verwalten
Pfeil 3.13 Zum Weiterlesen

Galileo Computing - Zum Seitenanfang

3.10 Spezielle thread-sichere DatenstrukturenZur nächsten Überschrift

Ein großer Unterschied zwischen den klassischen Datenstrukturen wie Vector oder Hashtable und denen aus der Collection-API besteht darin, dass alle Methoden durch synchronisierte Blöcke vor parallelen Änderungen geschützt waren. Bei den neuen Klassen wie ArrayList und HashMap sind Einfüge- und Löschoperationen nicht mehr automatisch synchronized. Sollen allerdings Listen, Mengen oder Assoziativspeicher vor nebenläufigen Änderungen sicher sein, gibt es zwei Möglichkeiten: einmal über spezielle sogenannte Wait-free- bzw. Lock-free-Algorithmen, die tatsächlich parallele Zugriffe – wenn möglich – ohne Lock erlauben, und einmal über synchronisierende Wrapper.


Galileo Computing - Zum Seitenanfang

3.10.1 Wait-free-AlgorithmenZur nächsten ÜberschriftZur vorigen Überschrift

Wenn zum Beispiel bei einer verketteten Liste ein Thread vorn etwas anhängt und der andere hinten etwas entfernt, ist das tatsächlich nebenläufig möglich, und es muss nicht die ganze Datenstruktur gelockt werden. Auch bei anderen Datenstrukturen kann direkt ohne Lock eine Operation durchgeführt werden, und es muss nur im Spezialfall eine besondere Absicherung vorgenommen werden.


Galileo Computing - Zum Seitenanfang

3.10.2 Nebenläufiger Assoziativspeicher und die Schnittstelle ConcurrentMapZur nächsten ÜberschriftZur vorigen Überschrift

Die Schnittstelle java.util.concurrent.ConcurrentMap erweitert die Schnittstelle java.util.Map um vier Operationen, die atomar ausgeführt werden müssen:

  • V putIfAbsent(K key, V value)
  • boolean remove(Object key, Object value)
  • V replace(K key, V value)
  • boolean replace(K key, V oldValue, V newValue)

Die Implementierungen der Schnittstelle sind:

  • ConcurrentHashMap: Ein Assoziativspeicher nach dem Hashing-Verfahren. Die Klasse implementiert ConcurrentNavigableMap (die wiederum NavigableMap erweitert), eine Schnittstelle, die Methoden zur Teilmengenbildung vorgibt.
  • ConcurrentSkipListMap: Ein Assoziativspeicher, der automatisch sortiert ist, also vergleichbar mit TreeMap.

Beide sind sehr schnelle Datenstrukturen für gleichzeitig operierende Threads, die dann auch keine ConcurrentModificationException auslösen, wenn es parallele Veränderungen über einen Iterator gibt.


Galileo Computing - Zum Seitenanfang

3.10.3 ConcurrentLinkedQueueZur nächsten ÜberschriftZur vorigen Überschrift

Obwohl es keine Schnittstellen ConcurrentSet und ConcurrentList gibt, existiert zumindest eine Klasse ConcurrentLinkedQueue, die eine thread-sichere und wartefreie Liste (genauer: Queue) ist. Wie der Name schon andeutet, beruht die Realisierung auf verketteten Listen und nicht auf Arrays. Ein eigenes ConcurrentSet könnte auf der Basis von ConcurrentHashMap implementiert werden, so wie auch HashSet intern mit einer HashMap realisiert ist. In Java 6 ist die Klasse ConcurrentSkipListSet hinzugekommen.


Galileo Computing - Zum Seitenanfang

3.10.4 CopyOnWriteArrayList und CopyOnWriteArraySetZur nächsten ÜberschriftZur vorigen Überschrift

Ist die Anzahl der Leseoperationen hoch, kann es sich lohnen, bei jedem Schreibzugriff erst die Daten zu kopieren und dann das Element hinzuzufügen, damit im Hintergrund andere Threads ohne einen Lock, der für das Schreiben nötig ist, Daten lesen können. Zwei dieser Datenstrukturen bietet Java an: CopyOnWriteArrayList für Listen und CopyOnWriteArraySet für Mengen. Die Klassen sind genau dann optimal, wenn wenig verändert – das ist teuer – und fast ausschließlich gelesen wird. Auch führen die Klassen zu keiner ConcurrentModificationException beim Ändern und gleichzeitigen Ablauf über Iteratoren. Denn falls die CopyOnWriteXXX-Datenstruktur verändert wird, arbeiten die »alten« Interessenten ja mit der herkömmlichen Version. Wenn zum Beispiel ein Iterator durch die Daten läuft und es dann eine Änderung gibt, so führt die Änderung zu einer Kopie der Daten und zur anschließenden Veränderung. Kommt eine Anfrage an die Datenstruktur nach der Veränderung, so bekommt der Anfrager Daten aus der neuen veränderten Datenstruktur. Kommt eine Anfrage während der Veränderung, also zu dem Zeitpunkt, an dem die Veränderung noch nicht abgeschlossen ist, so ist weiterhin die alte Version die einzig korrekte, und Daten kommen aus dieser CopyOnWriteXXX-Datenstruktur.


Galileo Computing - Zum Seitenanfang

3.10.5 Wrapper zur SynchronisationZur nächsten ÜberschriftZur vorigen Überschrift

Können zur Absicherung nebenläufiger Operationen die oben aufgeführten Datenstrukturen aus java.util.concurrent nicht verwendet werden, etwa bei Java 1.4 oder bei eigenen nicht-nebenläufig implementierten Versionen von Set, Map, List und Queue, lassen sich Zugriffe auf die Datenstrukturen extern synchronisieren. Dazu fordern statische Methoden wie synchronizedXXX() einen Wrapper an, der sich um die existierende Datenstruktur legt. Die Wrapper arbeiten mit einem Lock, und Parallelität in den Datenstrukturen ist nicht gegeben.

Beispiel

Eine synchronisierte Liste:

List<Byte> syncList = Collections.synchronizedList( new ArrayList<Byte>() );

Der generische Typ der Datenstruktur geht auch weiter an den Wrapper.

Die statischen synchronizedXXX()-Methoden liefern eine neue Sammlung, die sich wie eine Hülle um die existierende Datenstruktur legt und alle Methodenaufrufe synchronisiert. Paralleler Zugriff darf natürlich dann nur noch über den Wrapper laufen, wobei nicht-paralleler Zugriff weiterhin über die Original-Datenstruktur möglich ist.

class java.util.Collections
  • static <T> Collection<T> synchronizedCollection(Collection<T> c)
  • static <T> List<T> synchronizedList(List<T> list)
  • static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
  • static <T> Set<T> synchronizedSet(Set<T> s)
  • static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m)
  • static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s)
    Liefert synchronisierte, also thread-sichere Datenstrukturen.

Iteratoren von synchronisierten Wrappern

Mit dem Wrapper ist der nebenläufige Zugriff über die Methoden gesichert, aber nicht der Zugriff über den Iterator. Ist syncList eine synchronisierte Datenstruktur, die ein Iterator ablaufen möchte, und soll während des Ablaufens jeder andere Zugriff gesperrt sein, so ist Folgendes zu schreiben:

List<Byte> syncList = Collections.synchronizedList( new ArrayList<Byte>() );
synchronized ( syncList )

{
Iterator iter = syncList.iterator();
}

Die Synchronisation ist immer auf dem Wrapper und nicht auf dem »Gewrappten«.


Galileo Computing - Zum Seitenanfang

3.10.6 Blockierende WarteschlangenZur nächsten ÜberschriftZur vorigen Überschrift

Die Schnittstelle BlockingQueue steht für besondere Queues, die blockieren können. Das kann aus zwei Gründen geschehen: Entweder sind keine Daten zu entnehmen, da die Queue leer ist, oder eine maximale Anzahl von zu haltenden Elementen ist erreicht. Besonders in Produzenten/Konsumenten-Szenarien sind blockierende Warteschlangen sehr nützlich.

Eine performante thread-sichere Implementierung ist in der Praxis sehr wichtig, und die Java SE-Bibliothek hat zwei besondere Realisierungen auf Lager:

  • ArrayBlockingQueue: Queue immer mit einer maximalen Kapazität, intern realisiert mit einem Feld
  • LinkedBlockingQueue: Queue unbeschränkt oder mit maximaler Kapazität, intern realisiert durch eine verkettete Liste
  • PriorityBlockingQueue: eine blockierende PriorityQueue

Die anderen Realisierungen wie DelayQueue sind für uns jetzt nicht relevant.

Das Schöne an blockierenden Warteschlangen ist ihr Verhalten in Produzenten/Konsumenten-Verhältnissen; ein Thread (oder beliebig viele Threads) setzt (viele setzen) Daten in die Queue, ein Thread (oder beliebig viele Threads) holt (viele Threads holen) die Daten wieder raus. Bei einer Queue ist es ja so, dass sie nach dem FIFO-Verfahren arbeitet, das heißt, die Daten, die zuerst hineingelegt wurden, werden auch zuerst verarbeitet. Bei einer Prioritätswarteschlange ist das etwas anders, aber dazu gleich mehr.


Galileo Computing - Zum Seitenanfang

3.10.7 ArrayBlockingQueue und LinkedBlockingQueueZur nächsten ÜberschriftZur vorigen Überschrift

Bei den normalen blockierenden Datenstrukturen geht es darum, sich zwischen ArrayBlockingQueue und LinkedBlockingQueue zu entscheiden. Der wesentliche Unterschied ist – bis auf die interne Realisierung – dass die ArrayBlockingQueue immer mit einer maximalen Schranke von Elementen konfiguriert werden muss; ist diese Schranke erreicht, blockiert die Queue und nimmt keine neuen Elemente mehr an. Die LinkedBlockingQueue kann unbegrenzt wachsen (also bis Integer.MAX_VALUE). Ist die Queue dann offen, wird nur geblockt, wenn kein Element vorhanden ist, ein Thread aber eines entnehmen möchte.

Beispiel mit unbeschränkter LinkedBlockingQueue

Dazu ein einfaches Beispiel. Wir haben zwei Produzenten für Meldungen und einen Konsumenten, der sie nach Eingang einfach auf der Konsole ausgibt.

Listing 3.36: com/tutego/insel/thread/concurrent/LoggingInQueue.java, LoggingInQueue

public class LoggingInQueue
{
private static final BlockingQueue<String> messages =
new LinkedBlockingQueue<String>();

private static class MessageOutputter extends Thread
{
@Override public void run()
{
while ( true )
try
{
long startTime = System.currentTimeMillis();
System.out.printf( "%s (Wartete %d ms)%n",
messages.take(),
System.currentTimeMillis() – startTime ); }
catch ( InterruptedException e ) { }
}
}

private static class UserMessageProducer extends Thread
{
@Override public void run()
{
for( int i = 0; ; i++ )
messages.add( "msg " + i + " " +
JOptionPane.showInputDialog( "Meldung eingeben" ) );
}
}

private static class DiskspaceMessageProducer extends Thread
{
@Override public void run()
{
for( int i = 0; ; i++ )
{
String dir = System.getProperty( "user.dir" );
messages.add( "spc " + i + " " + new File( dir ).getFreeSpace() );
try { TimeUnit.SECONDS.sleep( 1 ); }
catch ( InterruptedException e ) { }
}
}
}

public static void main( String[] args )
{
new MessageOutputter().start();
new UserMessageProducer().start();
new DiskspaceMessageProducer().start();
}
}

Auf der einen Seite steht der Thread, der die blockiere Queue abhorcht. Wir fragen die Daten mit take() ab, damit die Methode – und somit der Thread – blockiert, falls keine Daten in der Queue sind. Die BlockingQueue-Schnittstelle definiert auch noch andere Methoden wie poll() oder peek(), aber die blockieren nicht, sondern liefern null, wenn keine Daten in der Queue sind – eigentlich sind die Methoden unpassend in einer blockierenden Datenstruktur, aber BlockingQueue erbt diese Methoden von Queue. (Leser sollten zum Test take() durch poll() ersetzen und das Ergebnis testen.) Um eine Vorstellung zu bekommen, wie lange take() warten muss, holen wir vor dem Aufruf von take() über System.currentTimeMillis() die Systemzeit in Millisekunden relativ zum 1.1.1970 sowie nach dem Aufruf von take() – die Differenz der Zeiten gibt uns eine Idee von der Dauer, während der der Thread wartet und auch keine Rechenzeit verbraucht.

Die Produzenten sind in unserem Fall zwei Threads. Der eine holt permanent die Anzahl freier Bytes auf der Festplatte des Benutzers, der andere Thread offeriert einen Benutzerdialog, und die Benutzereingabe kommt auf den Schirm. Gestartet mit ein paar Eingaben ist die Ausgabe dann auch recht unspektakulär:

lstspc 0 55943192576 (Wartete 11 ms)
spc 1 55942631424 (Wartete 943 ms)
spc 2 55943192576 (Wartete 1005 ms)
spc 3 55943192576 (Wartete 1007 ms)
msg 0 eingabe (Wartete 716 ms)
spc 4 55943192576 (Wartete 290 ms)
msg 1 eingabe (Wartete 822 ms)
spc 5 55943192576 (Wartete 180 ms)
msg 2 eingabe (Wartete 112 ms)

Galileo Computing - Zum Seitenanfang

3.10.8 PriorityBlockingQueueZur nächsten ÜberschriftZur vorigen Überschrift

Eine blockierende Prioritätswarteschlange ist eine besondere Prioritätswarteschlange, die thread-sicher ist und den Entnehmer-Thread blockiert, wenn kein Element vorhanden ist, also die Queue leer ist. Für blockierende Prioritätswarteschlangen bietet Java eine Klasse: PriorityBlockingQueue; sie implementiert PriorityQueue. Die Sortierung ist entweder natürlich oder über einen externen Comparator geregelt, bei Letzterem muss das Vergleichsobjekt im Konstruktor übergeben werden.

Tickets mit Priorität

Unser nächstes Beispiel legt Tickets in eine blockierende Prioritätswarteschlange. Tickets mit der höchsten Priorität sollen nach vorne wandern. Gleichzeitig messen wir die Zeit beim Anlegen des Tickets, um bei Tickets mit gleicher Priorität dem Ticket den Vorzug zu geben, das als erstes angelegt wurde.

Listing 3.37: com/tutego/insel/thread/concurrent/Ticket.java

package com.tutego.insel.util.concurrent;

import java.util.Date;

class Ticket implements Comparable<Ticket>
{
static enum Priority { SEVERE, NORMAL }

private final Priority priority;
private final String message;
private final Date arrivalDate;

Ticket( Priority priority, String message )
{
this.priority = priority;
this.message = message;
arrivalDate = new Date();
}

@Override public int compareTo( Ticket that )
{
int ticketPriority = this.priority.compareTo( that.priority );
// Wenn Ticket-Priorität ungleich 0, dann ist ein Ticket wichtiger als das
// andere und die Zeit spielt keine Rolle.
if ( ticketPriority != 0 )
return ticketPriority;
// Wenn Ticket-Priorität gleich 0, dann sind beide Tickets
// gleich wichtig. Die Zeit kommt dann als Kriterium hinzu.
return this.arrivalDate.compareTo( that.arrivalDate );
}

@Override public String toString()
{
return String.format( "%tT.%1$TL (%s) kam '%s'",
arrivalDate, priority, message );
}
}

Die Ticket-Klasse implementiert Comparable, sodass es eine natürliche Ordnung der Elemente gibt.

Ein Test, der die Ordnung bei der Sortierung zeigt, ist schnell geschrieben:

Listing 3.38: com/tutego/insel/thread/concurrent/TicketDemo.java, main()

List<Ticket> tickets = Arrays.asList(
new Ticket( Priority.NORMAL, "Kein Senf" ),
new Ticket( Priority.SEVERE, "Feuer" ),
new Ticket( Priority.NORMAL, "Bier warm" ),
new Ticket( Priority.SEVERE, "Erdbeben" ) );
Collections.sort( tickets );
System.out.println( tickets );

Die Ausgabe ist wie:

13:45:20.777 (SEVERE) kam 'Feuer'
13:45:20.777 (SEVERE) kam 'Erdbeben'
13:45:20.777 (NORMAL) kam 'Kein Senf'
13:45:20.777 (NORMAL) kam 'Bier warm'

Die Ausgabe macht zwei Dinge deutlich: Zunächst, dass die wichtigen Meldungen vorne liegen, und dann als zweites, dass zuerst eingefügte Meldungen auch zeitlich vor den nachfolgenden Meldungen liegen (wobei das Programm so schnell ist, dass die Zeit gar nicht als Komponente berücksichtigt werden kann).

Tickets generieren und mit der blockierenden Warteschlange verarbeiten

Im letzten Schritt können wir zwei Threads starten, wobei ein Thread neue Tickets (mit zufälliger Wichtigkeit) in die Queue legt und ein anderer Thread die Nachrichten permament entnimmt.

Listing 3.39: com/tutego/insel/thread/concurrent/PrioritizedTicketsQueue.java

package com.tutego.insel.util.concurrent;

import java.util.concurrent.*;
import com.tutego.insel.util.concurrent.Ticket.Priority;

public class PrioritizedTicketsQueue
{
private static final BlockingQueue<Ticket> tickets =
new PriorityBlockingQueue<Ticket>();

private static class TicketProducer extends Thread
{
@Override public void run()
{
while ( true )
{
Ticket ticket = new Ticket(
Priority.values()[ (int)(Math.random() * 2) ], "Hilfe!" );
tickets.add( ticket );
System.out.println( "Neues Ticket: " + ticket );
try { TimeUnit.MILLISECONDS.sleep( (int)(Math.random() * 2000) ); }
catch ( InterruptedException e ) { /* Empty */ }
}
}
}

private static class TicketSolver extends Thread
{
@Override public void run()
{
while ( true )
{
try
{
System.out.println( tickets.take() );
TimeUnit.SECONDS.sleep( 1 );
}
catch ( InterruptedException e ) { /* Empty */ }
}
}
}

public static void main( String[] args )
{
new TicketProducer().start();
new TicketSolver().start();
}
}

Lassen wir das Programm starten, kann die Ausgabe wie folgt sein:

Neues Ticket: 14:28:55.422 (SEVERE) kam 'Hilfe!'
14:28:55.422 (SEVERE) kam 'Hilfe!'
Neues Ticket: 14:28:56.038 (NORMAL) kam 'Hilfe!'
Neues Ticket: 14:28:56.445 (NORMAL) kam 'Hilfe!'
14:28:56.038 (NORMAL) kam 'Hilfe!'
Neues Ticket: 14:28:57.213 (NORMAL) kam 'Hilfe!'
Neues Ticket: 14:28:57.264 (NORMAL) kam 'Hilfe!'
Neues Ticket: 14:28:57.443 (SEVERE) kam 'Hilfe!'
14:28:57.443 (SEVERE) kam 'Hilfe!'

14:28:56.445 (NORMAL) kam 'Hilfe!'
Neues Ticket: 14:28:58.618 (SEVERE) kam 'Hilfe!'
Neues Ticket: 14:28:59.025 (SEVERE) kam 'Hilfe!'
14:28:58.618 (SEVERE) kam 'Hilfe!'
Neues Ticket: 14:29:00.418 (SEVERE) kam 'Hilfe!'
14:28:59.025 (SEVERE) kam 'Hilfe!'

Interessant ist die fett gedruckte Zeile, denn sie zeigt deutlich, dass – obwohl zwei Tickets vorher generiert wurden – diese durch die spätere Meldung verdrängt werden, da die spätere Meldung eine höhere Wichtigkeit hat als die beiden Meldungen zuvor.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.







<< zurück
  Zum Katalog
Zum Katalog: Java 7 – Mehr als eine Insel
Java 7 – Mehr als eine Insel
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Java und XML






 Java und XML


Zum Katalog: Einstieg in Eclipse 3.7






 Einstieg in
 Eclipse 3.7


Zum Katalog: Android 3






 Android 3


Zum Katalog: NetBeans Platform 7






 NetBeans Platform 7


Zum Katalog: Java ist auch eine Insel






 Java ist
 auch eine Insel


Zum Katalog: Apps entwickeln für Android 4






 Apps entwickeln
 für Android 4


Zum Katalog: Java 7






 Java 7


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Galileo Press 2012
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de