Hinweise zu Java1.5

Werbung
Java Concurrency Utilities
 Java unterstützt seit Java 1.0 Multithreading
 Java unterstützt das Monitorkonzept mittels der Schlüsselworte
synchronized und volatile sowie den java.lang.Object Methoden wait(),
notify() und notifyAll()
 Das ist eine vollständige Multithreading Unterstützung
 Aber: "Classical multi-threaded programming is very complex and error
prone". (Java Tuning White Paper von Sun)
 Synchronisationskontrukte (z.B. Barriere) wurden in Applikationen immer
wieder neu erfunden
 Eine Alternative zu Eigenbau Lösungen war lange Zeit die als Open Source
verfügbare util.concurrent Klassenbibliothek von Doug Lea
 Basierend auf diesen Erfahrungen wurde 2002 unter der Leitung von Doug
Lea der JSR 166 ins Leben gerufen mit dem Ziel "[to] provide functionality
commonly needed in concurrent programs"
 Das Ergebnis sind die sogenannten "Concurrency Utilities", die sich im
Package "java.util.concurrent" befinden und die seit Java 5 fester
Bestandteil des JDK sind
Java Concurrency Utilities
 Grundidee: fertige Bausteine den Anwendungsentwicklern zur Verfügung zu
stellen
 Bestandteile der Concurrency Utilities:
 Concurrent Collections: spezielle Implementierungen der Collection
Framework Interfaces Map, Set, List, Queue, Deque.
 Executor Framework: ein Framework zur Ausführung asynchroner Tasks
durch Threadpools.
 Synchronizers Diverse Hilfsklassen zur Koordination mehrerer Threads,
zum Beispiel Semaphore oder CyclicBarrier
 Locks und Conditions:
Flexiblere Objektrepräsentation des synchronized Schlüsselworts sowie der
java.lang.Object Monitor Methoden wait(), notify() und notifyAll().
 Atomic Variables: Erlauben die atomare Manipulation einzelner Variablen
(CAS - Compare And Set) zur Implementierung von Algorithmen ohne
Sperren (lock-free algorithms).
Java Executer
Zur parallelen Ausführung eines Runnable ist immer ein Thread notwendig. Obwohl die
nebenläufige Abarbeitung von Programmcode ohne Threads nicht möglich ist, sind doch beide
sehr stark verbunden, und es wäre gut, wenn das Runnable von dem tatsächlich abarbeitenden
Thread etwas getrennt wäre. Das hat mehrere Gründe:
Schon beim Erzeugen eines Thread-Objekts muss das Runnable-Objekt im Thread-Konstruktor
übergeben werden. Es ist nicht möglich, das Thread-Objekt aufzubauen, dann über eine
JavaBean-Setter-Methode das Runnable-Objekt zuzuweisen und anschließend den Thread mit
start() zu starten.
Wird start() auf dem Thread-Objekt zweimal aufgerufen, so führt der zweite Aufruf zu einer
Ausnahme. Ein erzeugter Thread kann also ein Runnable duch zweimaliges Aufrufen von start()
nicht gleich zweimal abarbeiten. Für eine erneute Abarbeitung eines Runnable ist also mit unseren
bisherigen Mitteln immer ein neues Thread-Objekt nötig.
Der Thread beginnt mit der Abarbeitung des Programmcodes vom Runnable sofort nach dem
Aufruf von start(). Die Implementierung vom Runnable selbst müsste geändert werden, wenn der
Programmcode nicht sofort, sondern später (nächste Tagesschau) oder wiederholt (immer
Weihnachten) ausgeführt werden soll.
Wünschenswert ist eine Abstraktion, die das Ausführen des Runnable-Programmcodes von der
technischen Realisierung (etwa den Threads) trennt.
ThreadPool
Eine wichtige statische Methode der Klasse Executors ist newCachedThreadPool(). Das Ergebnis
ist ein ExecutorService-Objekt, eine Implementierung von Executor mit der Methode
execute(Runnable):
Java Executer
Runnable r1 = new Runnable() {
@Override public void run() {
System.out.println( "A1 " + Thread.currentThread() );
System.out.println( "A2 " + Thread.currentThread() );
}
};
Runnable r2 = new Runnable() {
@Override public void run() {
System.out.println( "B1 " + Thread.currentThread() );
System.out.println( "B2 " + Thread.currentThread() );
}
};
ExecutorService executor = Executors.newCachedThreadPool();
Ausgaben:
executor.execute( r1 );
executor.execute( r2 );
Thread.sleep( 500 );
executor.execute( r1 );
executor.execute( r2 );
executor.shutdown();
A1 Thread[pool-1-thread-1,5,main]
A2 Thread[pool-1-thread-1,5,main]
B1 Thread[pool-1-thread-2,5,main]
B2 Thread[pool-1-thread-2,5,main]
B1 Thread[pool-1-thread-1,5,main]
B2 Thread[pool-1-thread-1,5,main]
A1 Thread[pool-1-thread-2,5,main]
A2 Thread[pool-1-thread-2,5,main]
Threads mit Rückgabe von Werten
mittels callable und future

Listing 2.13: com/tutego/insel/thread/concurrent/CallableDemo.java, SorterCallable

class SorterCallable implements Callable<byte[]>
{
private final byte[] b;
SorterCallable( byte[] b )
{
this.b = b;
}
@Override public byte[] call()
{
Arrays.sort( b );
return b;
// gibt es nicht bei runnable
}
}
Listing 2.14: com/tutego/insel/thread/concurrent/CallableDemo.java, main() Ausschnitt
byte[] b = new byte[ 4000000 ];
new Random().nextBytes( b );
Callable<byte[]> c = new SorterCallable( b );
ExecutorService executor = Executors.newCachedThreadPool();
Future<byte[]> result = executor.submit( c );
Der ExecutorService bietet eine submit()-Methode, die das Callable annimmt und einen Thread für die
Abarbeitung aussucht.
Threads mit Rückgabe von Werte
mittels callable und future
Listing 2.15: com/tutego/insel/thread/concurrent/CallableDemo.java, main()
byte[] b = new byte[ 4000000 ];
new Random().nextBytes( b );
Callable<byte[]> c = new SorterCallable( b );
ExecutorService executor = Executors.newCachedThreadPool();
Future<byte[]> result = executor.submit( c );
byte[] bs = result.get(); // blockiert erst jetzt, falls das Ergebnis noch nicht da ist
System.out.printf( "%d, %d, %d%n", bs[0], bs[1], bs[bs.length-1] ); // –128, –128, 127
 Vorteil: Ergebnisse von TASKS in Threads ausgeführt können angefordert werden.
Blockierungsfreies arbeiten möglich ....
Grob- und feingranulares Synchronisieren beim Erzeugen eines Singleton
Ein Singleton
 verhindert mehrfache Erzeugung der Objekte einer Klasse
 privater Konstruktor verhindert Erzeugung von mehreren Objekten
 static Methode erlaubt Erzeugung eines einzigen Objektes
 Objekt wird in privater Variable gespeichert
 weitere Aufrufe liefern das vorher erzeugte Objekt

static Methode muss bei parallelen Zugriff synchronisiert werden
//Threadsicherer Singleton
public class ST {
private static ST instance = null;
private ST() {}
public static synchronized ST getInstance(){
if (instance == null) {
instance = new ST();
}
return instance;
}
//Threadsicherer und schneller Singleton?
public class ST {
private static ST instance = null;
private ST() {}
public static ST getInstance(){
if (instance == null) { //first check
synchronized(ST.class) {
if (instance == null) { //second check
instance = new ST();
}
}
return instance;
}
Grob- und feingranulares Synchronisieren beim Erzeugen eines Singleton
public class ST { //Threadsicherer Singleton
private static ST instance = null;
private ST() {}
public static synchronized ST getInstance(){
if (instance == null){
instance = new ST();
}
return instance;
}
public class ST { //Threadsicherer u. schnell?
private static ST instance = null;
private ST() {}
public static ST getInstance(){
if (instance == null) { //first check
synchronized(ST.class) {
if (instance == null) { //second check
instance = new ST();
}
}
return instance; }}
public class ST { //nicht threadsicher!
private static ST instance = null;
private ST() {}
public static ST getInstance(){
if (instance == null) {
synchronized(ST.class) {
if (instance == null) {
instance = new ST();
}
}
return instance;}}
public class ST { Threadsicher und schnell !
private static volatile ST instance = null;
private ST() {}
public static ST getInstance(){
if (instance == null) {
synchronized(ST.class) {
if (instance == null) {
instance = new ST();
}
}
return instance;}}
Herunterladen