Verläufe in Java
Ein Verlauf ist ein zeitlich geordneter Ablauf innerhalb einer Programmausführung (eines Prozesses). Mehrere Verläufe können gleichzeitig oder fast gleichzeitig („parallel “) ablaufen.
Ein Dämonverlauf erbringt Dienste für andere Verläufe. Ein Verlauf, der kein Dämonverlauf ist, wird hier auch Hauptverlauf genannt. Ein Prozeß endet, wenn alle seine Hauptverläufe beendet sind.
Einem Verlauf kann auch ein Rang (zwischen Thread.MIN_PRIORITY [1] und Thread.MAX_PRIORITY (10), mit Thread.NORM_PRIORITY [5] als Vorgabe) zugeordnet werden, Verläufe mit höherem Rang werden bevorzugt ausgeführt.
Die Dämoneigenschaft und der Rang eines Verlaufs werden auch auf von diesem erzeugte Verläufe übertragen.
Beim Start der Javamaschine gibt es normalerweise einen Hauptverlauf, der die Methode "main" der beim Aufruf der Javamaschine angegebenen Klasse aufruft.
Die existierenden Verläufe werden dann von der Javamaschine solange ausgeführt bis die Methode "exit" der Klasse "Runtime" aktiviert wird (falls die Sicherheitsverwaltung die Ausführung der Operation "exit" zuläßt) oder alle Nichtdämonverläufe beendet wurden (weil die Ausführung ihrer Methode "run" beendet wurde oder eine Ausnahme aus dieser geworfen wurde).
Um einen neuen Verlauf zu erzeugen kann ein Exemplar der Klasse "Thread" (oder einer Erweiterung davon) erzeugt und durch Aktivierung der Methode "start" gestartet werden.
ExampleThread.java
class ExampleThread
extends java.lang.Thread
implements java.lang.Runnable{ public void run()
{ for( int i = 1; i > 0; ++i )System.out.println( i ); }}
Durch die Auswertung von "new ExampleThread().start()" wird dann der Verlauf erzeugt und die Methode "run" aktiviert. (Diese Methode entspricht der statischen Methode "main" eines Java-Programms).
Wenn die Operation "run" direkt aktiviert wird, dann wird diese zwar ausgeführt, aber innerhalb des aktivierenden Verlaufs.
Damit ein neuer Verlauf erzeugt wird, muß die (ererbte) Operation "start" aktiviert werden, welche dann einen neuen Verlauf erzeugt und darin die Methode "run" aktiviert.
Die Klausel "implements Runnable" wird im obigen Beispiel nicht benötigt. Sie kann aber verwendet werden, wenn ein anderer Weg zur Erzeugung eines Verlaufs gewünscht wird: Ein neuer Verlauf kann dann durch Auswertung des Ausdrucks "new Thread(new ExampleThread()).start()" gestartet werden.
Die Verwendung der Schnittstelle "java.lang.Runnable" ist im allgemeinen auch dann möglich, wenn die Erweiterung von "java.lang.Thread" nicht möglich ist, weil schon eine andere Klasse erweitert wird. Java erlaubt ja nur die Erweiterung höchstens einer Klasse. Daher erscheint die Verwendung dieser Schnittstelle als das empfehlenswerte Vorgehen.
Jeder Verlauf hat einen Namen, der aber nicht immer eindeutig nur einem Verlauf zugeordnet sein muß. Dieser Name des Verlaufs ist aber nicht mit dem Namen einer Referenzvariablen zu verwechseln, die ein Referenz auf einen Verlauf enthält. Solch eine Referenzvariable solte aufgelöst oder auf Null gesetzt werden, wenn sie nicht mehr benötigt wird, damit das entsprechende Objekt nicht länger als nötig existiert.
Beispiel.java
public final class Beispiel extends Thread
{ final private long interval;
final private int count;
public Beispiel
( final String n, final long li, final int ic )
{ super(n); interval = li * 1000; count = ic; }
public void run()
{ for( int i = 0; i < count; ++i )
{ try
{ System.out.println( getName() );
sleep(interval); }
catch (Exception x)
{ System.out.println( x ); }}}
public static void main(String[] args)
{ new Beispiel( "Verlauf0", 10, 20 ).start();
new Beispiel( "Verlauf1", 17, 15 ).start();
new Beispiel( "Verlauf2", 22, 11 ).start(); }}
EchoServer
final class EchoServer extends Thread
{ static final int ECHO_PORT = 7;
final java.net.Socket socket;
final EchoServer( final java.net.Socket socket )
throws java.io.IOException
{ this.socket = socket; }
final public void run()
{ java.io.InputStream in = null;
java.io.OutputStream out = null;
try
{ in = socket.getInputStream();
out = socket.getOutputStream();
byte[] b = new byte[ 16 ];
int c;
while(( c = in.read( b ))> 0 )out.write( b, 0, c ); }
catch( IOException e )
{ throw new java.lang.RuntimeException
( "IOException", e ); }
finally
{ try
{ in.close(); out.close(); s.close(); }
catch( IOException e )
{ throw new java.lang.RuntimeException
( "IOException", e ); }}}
final public static void main( final String[] args )
{ ServerSocket serverSocket = null;
try
{ serverSocket = new ServerSocket( 7 );
while( true )( new EchoServer( serverSocket.accept() )).start(); }
catch(Exception e){}
finally
{ try
{ serverSocket.close(); }
catch (IOException i)
{ throw new java.lang.RuntimeException
( "IOException", e ); }}}}
- Aussprachehinweise
- super ˈsupɚ (sd)
- Priorität einstellen
- Erzeugen Sie zwei neue Verlaufsobjekt und stellen Sie deren Priorität vor dem Starten auf unterschiedliche Werte. Verwenden Sie dabei die Schnittstelle "Runnable". Die beiden Objekte sollen etwas machen, an dem erkennbar wird, wie sie ihre unterschiedlichen Prioritäten auswirken. Die nötigen Operationen zum Einstellen der Prioritäten finden sich in der Quelle "http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Thread.html".
- (Die Priorität wird in der Praxis aber wihl selten verwendet.)
- Dämonen
- Erzeugen und starten Sie einen unbegrenzt lange laufenden Nichtdämonverlauf und lassen sie dann den ersten Hauptverlauf enden. Beobachten Sie, wie der Nichtdämonverlauf weiter läuft. Entnehmen Sie dann der Quelle "http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Thread.html", wie ein Dämonverlauf erzeugt wird und ändern Sie Ihr Programm so ab, daß statt des Nichtdämonverlaufs ein Dämonverlauf erzeugt wird. Beobachten Sie dann, wie dieser das Programm nicht mehr blockiert.
- Threads