8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE

advertisement
H 16. MULTITHREADING.
1. INLEIDING.

Threads: delen van het programma die in
concurrentie met elkaar gelijktijdig in executie
gaan.


Thread is een sequentiële besturingsstroom. Het
zijn ‘lichtgewicht’ processen.
Java voorziet primitieven voor multithreading.



Meeste programmeertalen moeten gebruik maken
van OS-primitieven voor multithreading en dus
platformspecifieke code gebruiken.
De werking van Java’s thread scheduling is wel
platform afhankelijk.
Een voorbeeld van multithreading is Java’s
garbage collector.
JAVA
1
2. THREAD TOESTANDEN: LEVENSCYCLUS.
Born
start
quantum expiration
yield
Waiting
sleep interval
expires
interrupt
thread dispatch
(assign a
processor)
I/O completes
acquire lock
interrupt
notify
notifyAll
timeout expires
interrupt
Ready
Running
Sleeping
Blocked
Bij voltooing van een thread
(terugkeer van run methode),
wordt de Dead toestand
bereikt (hier de final state)
JAVA
2
2. THREAD TOESTANDEN: LEVENSCYCLUS.

Thread toestanden.

Born state


Ready state (ook Runnable state)



Thread is juist gecreëerd.
start method van thread geactiveerd.
Thread kan nu in executie.
Running state

Thread is toegekend aan processor en in executie.


Dispatching the thread.
Dead state


Thread is beëindigd (taak volbracht of exit)
Garbage collector kan geheugen terug vrijgeven.
JAVA
3
2. THREAD TOESTANDEN: LEVENSCYCLUS.

Blocked state


Waiting state


Als een taak niet onmiddellijk kan volbracht worden (vb.
i/o verzoek).
Als een deel code nog niet kan uitgevoerd worden
(bepaalde vereisten moeten voldaan zijn)  thread
activeerd Object’s wait methode.
Sleeping state

Thread wacht tot slaaptijd is verstreken.
JAVA
4
3. THREAD PRIORITIES EN SCHEDULING.

Priority: indicatie van belangrijkheid (voorrang voor
processortijd).


Thread.MIN_PRIORITY (0) .. Thread.NORM_PRIORITY (5) ..
Thread.MAX_PRIORITY (10).
Thread scheduler: zorgt ervoor dat de thread met
hoogste prioriteit in Running state verkeert. Bij
meerdere threads met gelijke prioriteit wordt
timeslicing gebruikt. Elke thread krijgt een ‘quantum’
processortijd toegewezen.

Niet elk Java platform ondersteund timeslicing. De Thread
methode yield kan er dan voor zorgen dat threads van
gelijke prioiteit kunnen concurreren.
JAVA
5
3. THREAD PRIORITIES EN SCHEDULING.
Ready threads
Thread.MAX_PRIORITY
Priority 10
A
Priority 9
C
B
Priority 8
Thread.NORM_PRIORITY
Priority 7
D
E
Priority 6
G
Priority 5
H
I
J
K
F
Priority 4
Priority 3
Priority 2
Thread.MIN_PRIORITY
Priority 1
JAVA
6
4. CREATIE EN EXECUTIE VAN THREADS.

Demonstratie van sleep methode. Creatie van 3 threads met default
prioriteit die op willekeurig tijdsinterval een bericht tonen.
// class PrintThread bestuurt de thread executie
class PrintThread extends Thread
{ private int sleepTime;
public PrintThread( String name )
{ // geef de thread een naam
super( name );
// kies een willekeurige slaaptijd tussen 0 and 5 seconde
sleepTime = ( int ) ( Math.random() * 5001 );
}
JAVA
7
4. CREATIE EN EXECUTIE VAN THREADS.
// method run wordt automatisch geactiveerd bij een nieuwe thread
public void run()
{ // breng thread in sleep toestand voor sleepTime seconde
try
{ System.err.println( getName() + " going to sleep for " + sleepTime );
Thread.sleep( sleepTime );
}
// als de thread gedurende de sleep toestand wordt onderbroken, print stack trace
catch ( InterruptedException exception )
{ exception.printStackTrace();
}
// print thread name
System.err.println( getName() + " done sleeping" );
}
} // einde class PrintThread
JAVA
8
4. CREATIE EN EXECUTIE VAN THREADS.
// Meerdere threads die op verschillende tijdsintervallen printen.
public class ThreadTester
{ public static void main( String [] args )
{ // creatie van de drie threads
PrintThread thread1 = new PrintThread( "thread1" ); //thread1 in Born toestand
PrintThread thread2 = new PrintThread( "thread2" ); //thread1 in Born toestand
PrintThread thread3 = new PrintThread( "thread3" ); //thread1 in Born toestand
System.err.println( "Starting threads" );
thread1.start(); // Plaats thread1 in Ready toestand
thread2.start(); // Plaats thread2 in Ready toestand
thread3.start(); // Plaats thread3 in Ready toestand
System.err.println( "Threads started, main ends\n" );
}
}
JAVA
9
4. CREATIE EN EXECUTIE VAN THREADS.


Als een thread voor de eerste keer in de Running toestand komt wordt
de methode run geactiveerd.
Ook al is de maincode beëindigd, het programma eindigt pas als alle
threads de Dead toestand bereikt hebben.
Starting threads
Threads started, main ends
thread1
thread2
thread3
thread3
thread1
thread2
going to sleep for 1217
going to sleep for 3989
going to sleep for 662
done sleeping
done sleeping
done sleeping
Starting threads
thread1 going to sleep for 314
thread2 going to sleep for 1990
Threads started, main ends
thread3
thread1
thread2
thread3
JAVA
going to sleep for 3016
done sleeping
done sleeping
done sleeping
10
5. THREAD SYNCHRONISATIE.





Meerdere threads kunnen een object delen, ‘shared object’.
Wanneer meerdere threads het shared object kunnen wijzigen
kunnen er problemen ontstaan.
  mutual exclusion of thread synchronisatie.
  in Java gebruik van monitors voor synchronisatie.
Elk object heeft een monitor: geeft maar één thread tegelijkertijd
executierecht bij een synchronized statement op het object,
‘obtaining the lock’. Andere threads komen in Blocked toestand. Als
de lock wordt vrijgegeven, ‘released’, zal de monitor de geblokte
thread met hoogste priority laten voortgaan.
Een volledige methode kan synchronized zijn.
Deadlock preventie: als een thread de wait methode activeert, zorg
dan dat een afzonderlijke thread de notify methode activeerd voor
overgang naar de Ready toestand.
JAVA
11
6. PRODUCER/CONSUMER RELATIE ZONDER
SYNCHRONISATIE.



Producer: genereert data en bewaart het in een shared object.
Consumer: leest de data van het shared object.
In multithreaded applicatie:
 Componenten: producerthread, consumerthread en buffer.
 Producerthread:
Activeer wait: als vorige data nog niet verwerkt is,
consumer kan data ophalen.
Activeer notify: als nieuwe data in buffer geplaatst is.
 Consumerthread:
Activeer notify: als de data opgehaald is, producer kan
nieuwe data in buffer plaatsen.
Activeer wait: als buffer leeg is of vorige data nog
aanwezig is.
JAVA
12
6. PRODUCER/CONSUMER RELATIE ZONDER
SYNCHRONISATIE.

Mogelijke logische fouten als we geen synchronisatie
gebruiken: VOORBEELD SharedBufferTest.



Producer genereert een reeks getallen van 1 tot 4. Consumer
telt het aantal getallen en sommeert die getallen. Bij beide
threads is een random vertraging ingebouwd (0 tot 3 seconden
sleep) om een reëel programma te simuleren. Bij multithreaded
applicaties is het ongekend wanneer een thread zijn taak
uitvoert en hoelang dat duurt.
Beide threads drukken ook de getallen af.
Componenten: Interface Buffer en Klassen Producer, Consumer,
UnsynchronizedBuffer en SharedBufferTest.
JAVA
13
6. PRODUCER/CONSUMER RELATIE ZONDER
SYNCHRONISATIE.
public interface Buffer
{ public void set( int value ); // plaats een waarde in de buffer
public int get();
// haal een waarde uit de buffer
}
public class Producer extends Thread
{ private Buffer sharedLocation; // reference naar shared object
public Producer( Buffer shared )
{ super( "Producer" );
sharedLocation = shared;
}
JAVA
14
6. PRODUCER/CONSUMER RELATIE ZONDER
SYNCHRONISATIE.
public void run() // berg waarden 1 tot 4 op in sharedLocation
{
for ( int count = 1; count <= 4; count++ )
{ try
{ // sleep 0 to 3 seconde en plaats waarde in buffer
Thread.sleep( ( int ) ( Math.random() * 3001 ) );
sharedLocation.set( count );
}
// indien sleeping thread interrupted druk stack trace
catch ( InterruptedException exception )
{ exception.printStackTrace();
}
}
System.err.println( getName() + " done producing." + "\nTerminating " + getName() + ".");
}
} // einde class Producer
JAVA
15
6. PRODUCER/CONSUMER RELATIE ZONDER
SYNCHRONISATIE.
public class Consumer extends Thread
{ private Buffer sharedLocation; // reference naar shared object
public Consumer( Buffer shared )
{ super( "Consumer" );
sharedLocation = shared;
}
JAVA
16
6. PRODUCER/CONSUMER RELATIE ZONDER
SYNCHRONISATIE.
public void run() // lees sharedLocation's waarde 4 keer en sommeer de waarden
{ int sum = 0;
for ( int count = 1; count <= 4; count++ )
{ try // sleep 0 to 3 seconden en lees waarde uit de buffer en tel bij som
{ Thread.sleep( ( int ) ( Math.random() * 3001 ) );
sum += sharedLocation.get();
}
// indien sleeping thread interrupted druk stack trace
catch ( InterruptedException exception )
{ exception.printStackTrace();
}
}
System.err.println( getName() + " read values totaling: " + sum +
".\nTerminating " + getName() + ".");
} } // einde class Consumer
JAVA
17
6. PRODUCER/CONSUMER RELATIE ZONDER
SYNCHRONISATIE.
public class UnsynchronizedBuffer implements Buffer
{ private int buffer = -1; // gedeeld door producer en consumer threads
// plaats waarde in buffer
public void set( int value )
{ System.err.println( Thread.currentThread().getName() + " writes " + value );
buffer = value;
}
// return waarde uit buffer
public int get()
{ System.err.println( Thread.currentThread().getName() + " reads " + buffer );
return buffer;
}
}
JAVA
18
6. PRODUCER/CONSUMER RELATIE ZONDER
SYNCHRONISATIE.
public class SharedBufferTest
{ public static void main( String [] args )
{ // instantiatie van het shared object gebruikt door de threads
Buffer sharedLocation = new UnsynchronizedBuffer();
// instantiatie van producer and consumer objecten
Producer producer = new Producer( sharedLocation );
Consumer consumer = new Consumer( sharedLocation );
producer.start(); // start producer thread
consumer.start(); // start consumer thread
}
}
JAVA
19
6. PRODUCER/CONSUMER RELATIE ZONDER
SYNCHRONISATIE.
Consumer reads -1
Producer writes 1
Consumer reads 1
Consumer reads 1
Consumer reads 1
Consumer read values totaling: 2.
Terminating Consumer.
Producer writes 2
Producer writes 3
Producer writes 4
Producer done producing.
Terminating Producer.
Producer writes 1
Producer writes 2
Consumer reads 2
Producer writes 3
Consumer reads 3
Producer writes 4
Producer done producing.
Terminating Producer.
Consumer reads 4
Consumer reads 4
Consumer read values totaling: 13.
Terminating Consumer.
JAVA
20
6. PRODUCER/CONSUMER RELATIE ZONDER
SYNCHRONISATIE.
Producer writes 1
Consumer reads 1
Producer writes 2
Consumer reads 2
Producer writes 3
Consumer reads 3
Producer writes 4
Producer done producing.
Terminating Producer.
Consumer reads 4
Consumer read values totaling: 10.
Terminating Consumer.
JAVA
21
7. PRODUCER/CONSUMER RELATIE MET
SYNCHRONISATIE.




Uit vorige voorbeeld blijkt synchronisatie noodzakelijk.
Threads die toegang nemen tot een shared object merken zelf niets van de
synchronisatie. Synchronisatiecode komt in de set en get methoden van
SynchronizedBuffer.
Er wordt een extra attribuut gebruikt: int occupiedBufferCount. Dit is
een ‘condition’ variabele. Hiermee wordt de communicatie geregeld, mag de
buffer gevuld/geledigd worden?
Het activeren van de wait methode brengt de thread in Wait toestand
waardoor de blokkering voor het gesynchroniseert object beëindigd wordt
(release lock).
public class SynchronizedBuffer implements Buffer
{ private int buffer = -1; // // gedeeld door producer en consumer threads
private int occupiedBufferCount = 0; // geeft aantal bezette buffers
JAVA
22
7. PRODUCER/CONSUMER RELATIE MET
SYNCHRONISATIE.
public synchronized void set( int value ) // plaats waarde in buffer
{ String name = Thread.currentThread().getName(); // voor afdrukken status thread
while ( occupiedBufferCount == 1 ) // zolang buffer niet leeg, plaats thread in Wait toestand
{ try // afdrukken thread en buffer status
{ System.err.println( name + " tries to write." );
displayState( "Buffer full. " + name + " waits." );
wait();
}
catch ( InterruptedException exception ) { exception.printStackTrace(); }
}
buffer = value; // plaats nieuwe waarde in buffer
++occupiedBufferCount; // registreer dat buffer gevuld is… consumer moet buffer ledigen
displayState( name + " writes " + buffer );
notify(); // verwittig de thread in Wait toestand om naar Ready toestand over te gaan
JAVA
} // einde methode set; beëindig blokkering op SynchronizedBuffer
(release lock)
23
7. PRODUCER/CONSUMER RELATIE MET
SYNCHRONISATIE.
public synchronized int get() // levert waarde uit buffer
{ String name = Thread.currentThread().getName();
while ( occupiedBufferCount == 0 ) // zolang buffer leeg, plaats thread in Wait toestand
{ try // afdrukken thread en buffer status
{ System.err.println( name + " tries to read." );
displayState( "Buffer empty. " + name + " waits." );
wait();
}
catch ( InterruptedException exception ) { exception.printStackTrace(); }
}
--occupiedBufferCount; // registreer dat buffer leeg is… producer moet buffer vulllen
displayState( name + " reads " + buffer );
notify(); // verwittig de thread in Wait toestand om naar Ready toestand over te gaan
return buffer ;
} // einde methode get; beëindig blokkering opJAVA
SynchronizedBuffer (release lock)
24
7. PRODUCER/CONSUMER RELATIE MET
SYNCHRONISATIE.
// toon huidige operatie en buffer status
public void displayState( String operation )
{ StringBuffer outputLine = new StringBuffer( operation );
outputLine.setLength( 40 );
outputLine.append( buffer + "\t\t" + occupiedBufferCount );
System.err.println( outputLine );
System.err.println();
}
} // einde class SynchronizedBuffer
•
Zonder notify blijft de andere thread in Waiting toestand en
veroorzaakt deadlock.
•
Indien met meer dan 2 threads: gebruik notifyAll.
JAVA
25
7. PRODUCER/CONSUMER RELATIE MET
SYNCHRONISATIE.
public class SharedBufferTest2
{ public static void main( String [] args )
{ SynchronizedBuffer sharedLocation = new SynchronizedBuffer();
StringBuffer columnHeads = new StringBuffer( "Operation" );
columnHeads.setLength( 40 ); columnHeads.append( "Buffer\t\tOccupied Count" );
System.err.println( columnHeads ); System.err.println();
sharedLocation.displayState( "Initial State" );
// instantiatie van producer and consumer objecten
Producer producer = new Producer( sharedLocation );
Consumer consumer = new Consumer( sharedLocation );
producer.start(); // start producer thread
consumer.start(); // start consumer thread
}
}
JAVA
26
7. PRODUCER/CONSUMER RELATIE MET
SYNCHRONISATIE. Output 1.
Operation
Buffer
Occupied Count
Initial State
-1
0
Buffer empty. Consumer waits.
-1
0
Producer writes 1
1
1
Consumer reads 1
1
0
Consumer tries to read.
Buffer empty. Consumer waits.
1
0
Producer writes 2
2
1
Consumer reads 2
2
0
Producer writes 3
3
1
Consumer tries to read.
JAVA
27
7. PRODUCER/CONSUMER RELATIE MET
SYNCHRONISATIE. Output 1.
Vervolg…
Consumer reads 3
3
0
Consumer tries to read.
Buffer empty. Consumer waits.
3
0
Producer writes 4
4
1
Consumer reads 4
Producer done producing.
Terminating Producer.
4
0
Consumer read values totaling: 10.
Terminating Consumer.
JAVA
28
7. PRODUCER/CONSUMER RELATIE MET
SYNCHRONISATIE. Output 2.
Operation
Buffer
Occupied Count
Initial State
-1
0
Consumer tries to read.
Buffer empty. Consumer waits.
-1
0
Producer writes 1
1
1
Consumer reads 1
1
0
Producer writes 2
Producer tries to write.
Buffer full. Producer waits.
2
1
2
1
Consumer reads 2
2
0
Producer writes 3
3
1
Consumer reads 3
3
0
Producer writes 4
4
1
JAVA
29
7. PRODUCER/CONSUMER RELATIE MET
SYNCHRONISATIE. Output 2.
Vervolg…
Producer done producing.
Terminating Producer.
Consumer reads 4
4
0
Consumer read values totaling: 10.
Terminating Consumer.
JAVA
30
7. PRODUCER/CONSUMER RELATIE MET
SYNCHRONISATIE. Output 3.
Operation
Buffer
Occupied Count
Initial State
-1
0
Producer writes 1
1
1
Consumer reads 1
1
0
Producer writes 2
Consumer reads 2
2
2
1
0
Producer writes 3
3
1
Consumer reads 3
3
0
Producer writes 4
4
1
Producer done producing.
Terminating Producer.
Consumer reads 4
4
0
Consumer read values totaling: 10.
Terminating Consumer.
JAVA
31
OEFENING SYNCHRONISATIE: RESTAURANT
Simuleer de werking in een restaurant: er is één kok die orders
klaarmaakt en er zijn twee kelners (Sofie en Hendrik) die de orders
naar de klant brengen. Stel een random vertraging in van 0 tot 2
seconden bij de kok en de kelners.
Na 10 orders sluit het restaurant (voedsel is op).
JAVA
32
OEFENING SYNCHRONISATIE: RESTAURANT
Gebruik onderstaande klasse voor de orders:
class Order
{ private static int i = 1;
private int count = i++;
public Order()
{ if (count > 10)
{ System.out.println("Voedsel is op, sluiten!");
System.exit(0);
}
}
public String toString()
{ return "Order " + count;
}
}
JAVA
33
class Kelner extends Thread
OPLOSSING SYNCHRONISATIE: RESTAURANT
{ private Restaurant restaurant;
private String naam;
public Kelner(Restaurant r, String n)
{ restaurant = r; naam = n;
start();
}
public void run()
{ while(true)
{ try
{ sleep((int) (Math.random()*2001));
}
catch(InterruptedException e) { throw new RuntimeException(e); }
Order order = restaurant.getOrder();
System.out.println("Kelner " + naam + " krijgt " + order);
}
}
}
JAVA
34
class Kok extends Thread
{ private Restaurant restaurant;
OPLOSSING SYNCHRONISATIE:
RESTAURANT
public Kok(Restaurant r)
{ restaurant = r;
start();
}
public void run()
{ while(true)
{ try
{ sleep((int) (Math.random()*2001));
restaurant.setOrder(new Order());
}
catch(InterruptedException e) { throw new RuntimeException(e); }
}
}
}
JAVA
35
OPLOSSING SYNCHRONISATIE: RESTAURANT
public class Restaurant
{ private Order order;
public synchronized void setOrder(Order o)
{ while (order!=null)
try
{ wait();
}
catch(InterruptedException e) { throw new RuntimeException(e); }
order=o;
notifyAll();
}
JAVA
36
OPLOSSING SYNCHRONISATIE: RESTAURANT
public synchronized Order getOrder()
{ while (order==null)
{ try
{ wait();
}
catch(InterruptedException e) { throw new RuntimeException(e); }
}
Order ref=order;
order = null;
notifyAll();
return ref;
}
JAVA
37
OPLOSSING SYNCHRONISATIE: RESTAURANT
public static void main(String[] args)
{ Restaurant restaurant = new Restaurant();
new Kelner(restaurant, "Sofie");
new Kelner(restaurant, "Hendrik");
new Kok(restaurant);
}
}
JAVA
38
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER.




Synchronisatie zoals in vorig punt kan tot inefficiëntie leiden, er
kan veel tijd verloren gaan in de Wait toestand.
 Als de producer sneller is, moet die steeds wachten op de
consumer om de volgende waarde te kunnen leveren.
 Als de consumer sneller is, moet die steeds wachten op de
producer die de waarde levert.
 Zelfs als ze ongeveer even snel zijn zullen ze op elkaar
moeten wachten omdat ze ‘tegenover elkaar verlopen’.
We voorzien extra buffer ruimte : een circulaire buffer.
De circulaire buffer is enkel wanneer beide threads ongeveer
even snel zijn. De juiste grootte van de buffer is cruciaal om de
thread-wait tijd te minimaliseren.
Het voorbeeld gebruikt een circulaire buffer van 3 elementen en
werkt met Swing componenten. Swing componenten zijn niet
thread-safe. Daarom gebruiken we een event-dispatching thread.
JAVA
39
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER.



Alle interactie met swingGUIcomponenten mag niet door
meerdere threads ‘tegelijkertijd’ gebeuren.
De swing klasse SwingUtilities voorziet de statische methode
invokeLater. Het argument moet een object zijn dat de interface
Runnable (package java.lang) implementeert. Runnable heeft
als enige methode void run(). Ieder thread is een Runnable
object.
invokeLater zal GUI verwerkende opdrachten laten uitvoeren als
deel van de event-dispatching thread. De uitvoering
gebeurt nadat alle in verzoek zijnde (‘pending’) AWT events zijn
verwerkt. invokeLater is asynchroon, wacht niet tot de opdracht
is uitgevoerd. invokeAndWait is synchroon, opgelet voor
deadlock.
JAVA
40
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER. Class RunnableOutput.
import javax.swing.*;
public class RunnableOutput implements Runnable
{ private JTextArea outputArea;
private String messageToAppend;
public RunnableOutput( JTextArea output, String message )
{ outputArea = output; // een referentie naar de JTextArea waar de output naar toe moet
messageToAppend = message; // de boodschap die in JTextArea moet toegevoegd worden
}
public void run() // wordt door SwingUtilities.invokeLater gebruikt voor activatie
{ outputArea.append( messageToAppend );
}
}
JAVA
41
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER. Class Producer.
// Producer’s run methode is een thread die de waarden 11 tot 20 opslaat in sharedLocation.
import javax.swing.*;
public class Producer extends Thread
{ private Buffer sharedLocation; // reference naar shared object
private JTextArea outputArea;
public Producer( Buffer shared, JTextArea output )
{ super( "Producer" );
sharedLocation = shared;
outputArea = output;
}
JAVA
42
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER. Class Producer.
public void run()
{ for ( int count = 11; count <= 20; count ++ )
{ try // sleep 0 tot 3 seconde voordat de waarde in Buffer wordt geplaatst
{ Thread.sleep( ( int ) ( Math.random() * 3000 ) );
sharedLocation.set( count );
}
catch ( InterruptedException exception ) { exception.printStackTrace(); }
}
String name = getName();
SwingUtilities.invokeLater( new RunnableOutput( outputArea, "\n" +
name + " done producing.\n" + name + " terminated.\n" ) );
} // einde methode run
} // einde class Producer
JAVA
43
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER. Class Consumer.
// Consumer’s run methode is een thread die 10 maal een waarde ophaalt uit sharedLocation.
import javax.swing.*;
public class Consumer extends Thread
{ private Buffer sharedLocation; // reference naar shared object
private JTextArea outputArea;
public Consumer( Buffer shared, JTextArea output )
{ super( "Consumer" );
sharedLocation = shared;
outputArea = output;
}
JAVA
44
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER. Class Consumer.
public void run()
{ int sum = 0;
for ( int count = 1; count <= 10; count++ )
{ try // sleep 0 tot 3 seconde voordat de waarde uit Buffer wordt gelezen en gesommeerd
{ Thread.sleep( ( int ) ( Math.random() * 3001 ) );
sum += sharedLocation.get();
}
catch ( InterruptedException exception ) { exception.printStackTrace(); }
}
String name = getName();
SwingUtilities.invokeLater( new RunnableOutput( outputArea,
"\nTotal " + name + " consumed: " + sum + ".\n" +
name + " terminated.\n ") );
}
} // einde class Consumer
JAVA
45
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER. Class CircularBuffer.
// CircularBuffer synchroniseert toegang tot een array van shared buffers.
import javax.swing.*;
public class CircularBuffer implements Buffer
{ private int buffers[] = { -1, -1, -1 }; // elk array element is een buffer
private int occupiedBufferCount = 0;
private int readLocation = 0, writeLocation = 0; // onderhouden de lees/schrijf positie in de array
private JTextArea outputArea; // reference naar GUI component voor output
public CircularBuffer( JTextArea output )
{ outputArea = output;
}
JAVA
46
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER. Class CircularBuffer.
public synchronized void set( int value ) // plaats waarde in buffer
{ String name = Thread.currentThread().getName();
while ( occupiedBufferCount == buffers.length ) // zolang er geen plaats
{ try // afdrukken thread en buffer status, plaats thread in Wait toestand
{ SwingUtilities.invokeLater( new RunnableOutput( outputArea,
"\nAll buffers full. " + name + " waits." ) );
wait();
}
catch ( InterruptedException exception ) { exception.printStackTrace(); }
}
JAVA
47
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER. Class CircularBuffer.
buffers[ writeLocation ] = value; // plaats waarde op writeLocation in buffers
SwingUtilities.invokeLater( new RunnableOutput( outputArea, // update Swing GUI component
"\n" + name + " writes " + buffers[ writeLocation ] + " ") );
++occupiedBufferCount; // en verhoog het aantal bezette buffers
writeLocation = ( writeLocation + 1 ) % buffers.length; // update voor toekomstige schrijfoperatie
SwingUtilities.invokeLater( new RunnableOutput( // toon de inhoud van de shared buffers
outputArea, createStateOutput() ) );
notify(); // breng waiting thread (als er een is) terug naar ready toestand
} // einde methode set
JAVA
48
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER. Class CircularBuffer.
public synchronized int get() // levert waarde uit buffer
{ String name = Thread.currentThread().getName();
// while no data to read, place thread in waiting state
{ try // afdrukken thread en buffer status, plaats thread in Wait toestand
{ SwingUtilities.invokeLater( new RunnableOutput( outputArea,
"\nAll buffers empty. " + name + " waits.") );
wait();
}
catch ( InterruptedException exception ) { exception.printStackTrace(); }
}
JAVA
49
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER. Class CircularBuffer.
int readValue = buffers[ readLocation ]; // haal waarde op readLocation uit buffers
SwingUtilities.invokeLater( new RunnableOutput( outputArea, // update Swing GUI component
"\n" + name + " reads " + readValue + " ") );
--occupiedBufferCount; // er is een buffer vrijgekomen
readLocation = ( readLocation + 1 ) % buffers.length; // update voor toekomstige leesoperatie
SwingUtilities.invokeLater( new RunnableOutput( // toon de inhoud van de shared buffers
outputArea, createStateOutput() ) );
notify(); // breng waiting thread (als er een is) terug naar ready toestand
return readValue;
} // einde methode get
JAVA
50
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER. Class CircularBuffer.
public String createStateOutput()
{ // eerste lijn status informatie
String output = "(buffers occupied: " + occupiedBufferCount + ")\nbuffers: ";
for ( int i = 0; i < buffers.length; i++ )
output += " " + buffers[ i ] + " ";
// tweede lijn status informatie
output += "\n
";
for ( int i = 0; i < buffers.length; i++ )
output += "---- ";
// derde lijn status informatie
output += "\n
“;
JAVA
51
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER. Class CircularBuffer.
// plaats indicators R(readLocation) en W (writeLocation)
// onder de betreffende buffer locaties
for ( int i = 0; i < buffers.length; i++ )
if ( i == writeLocation && writeLocation == readLocation )
output += " WR ";
else if ( i == writeLocation )
output += " W ";
else if ( i == readLocation )
output += " R ";
else
output += "
";
output += "\n";
return output;
} // einde methode createStateOutput
} // einde class CircularBuffer
JAVA
52
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER. Class CircularBufferTest.
// CircularBufferTest toont twee threads die gebruik maken van een circulaire buffer.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
// creatie en activatie van de producer en consumer threads
public class CircularBufferTest extends JFrame
{ JTextArea outputArea;
// opbouw GUI
public CircularBufferTest()
{ super( "Demonstrating Thread Synchronizaton" );
outputArea = new JTextArea( 20,30 );
outputArea.setFont( new Font( "Monospaced", Font.PLAIN, 12 ) );
getContentPane().add( new JScrollPane( outputArea ) );
setSize( 310, 500 );
setVisible( true );
JAVA
53
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER. Class CircularBufferTest.
// creatie van het shared object CircularBuffer dat gedeeld zal worden door beide threads
CircularBuffer sharedLocation = new CircularBuffer( outputArea );
// afdrukken van de initiële status van de buffers in CircularBuffer
SwingUtilities.invokeLater( new RunnableOutput( outputArea,
sharedLocation.createStateOutput() ) );
// creatie van de producer en consumer threads
Producer producer = new Producer( sharedLocation, outputArea );
Consumer consumer = new Consumer( sharedLocation, outputArea );
producer.start(); // start producer thread
consumer.start(); // start consumer thread
} // einde constructor
public static void main ( String args[] )
{ CircularBufferTest application = new CircularBufferTest();
application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
}
} // einde class CirclularBufferTest
JAVA
54
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER.
JAVA
Waarde in laatste buffer
geplaatst. De volgende
waarde komt in de eerste
buffer.
55
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER.
Circulair buffer effect—de
vierde waarde wordt in de
eerste buffer geplaatst.
Waarde in laatste buffer
geplaatst. De volgende waarde
komt in de eerste buffer.
JAVA
Circulair buffer effect—de
zevende waarde wordt in de
eerste buffer geplaatst.
56
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER.
Waarde in laatste buffer
geplaatst. De volgende waarde
komt in de eerste buffer.
JAVA
Circulair buffer effect—de tiende
waarde wordt in de eerste buffer
57
geplaatst.
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE
BUFFER.
JAVA
58
9. DEAMON THREADS.

Een thread die als ondersteuning van andere threads meedraait.
De deamon thread verhindert niet dat een programma
beëindigd wordt.
 Java’s garbage collector is een voorbeeld van een deamon
thread.
 We markeren een thread als deamon met de methode
setDeamon(true). Dit moet wel gebeuren voordat de start
methode van de thread geactiveerd wordt.
JAVA
59
10. RUNNABLE INTERFACE.


Een klasse die al subklasse is kan niet meer erven van Thread;
er is geen meervoudige overerving in Java toegelaten.
Dergelijke klasse kan wel de interface Runnable implementeren
(zoals overigens ook klasse Thread doet).
De klasse thread voorziet vijf constructors met een Runnable
object als argument.



public Thread(Runnable runnableObject)
public Thread(Runnable runnableObject, String threadName)
VOORBEELD: een applet met twee innerklassen die de interface
Runnable implementeren.



Eén voor beheer van threads aangemaakt in de applet.
Eén voor GUI updates.
Codeertechnieken voor suspend, resume en stop worden
geïllustreerd (afgekeurde methoden van Thread).

Door thread synchronisatie en Objects methode wait en notify.
JAVA
60
10. RUNNABLE INTERFACE.
JAVA
61
10. RUNNABLE INTERFACE.
// Class RandomCharacters demonstreert the Runnable interface
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class RandomCharacters extends JApplet implements ActionListener
{ private String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private final static int SIZE = 3;
private JLabel outputs[];
private JCheckBox checkboxes[];
private Thread threads[];
private boolean suspended[];
JAVA
62
10. RUNNABLE INTERFACE.
public void init() //aanmaak GUI en arrays
{ outputs = new JLabel[ SIZE ];
threads = new Thread[ SIZE ];
checkboxes = new JCheckBox[ SIZE ];
suspended = new boolean[ SIZE ];
Container container = getContentPane();
container.setLayout( new GridLayout( SIZE, 2, 5, 5 ) );
for ( int count = 0; count < SIZE; count++ )
{ outputs[ count ] = new JLabel();
outputs[ count ].setBackground( Color.GREEN );
outputs[ count ].setOpaque( true );
container.add( outputs[ count ] );
checkboxes[ count ] = new JCheckBox( "Suspended" );
checkboxes[ count ].addActionListener( this );
container.add( checkboxes[ count ] );
}
} // end method init
JAVA
63
10. RUNNABLE INTERFACE.
public void start() // creatie en start threads telkens start methode van de Applet wordt geactiveerd
{ for ( int count = 0; count < threads.length; count++ )
{ // create Thread; initialize object that implements Runnable
threads[ count ] = new Thread( new RunnableObject(), "Thread " + ( count + 1 ) );
threads[ count ].start(); // begin executing Thread
}
}
private int getIndex( Thread current ) // bepaal thread locatie in threads array
{ for ( int count = 0; count < threads.length; count++ )
if ( current == threads[ count ] )
return count;
return -1;
}
JAVA
64
10. RUNNABLE INTERFACE.
public synchronized void stop() // stop alle threads als stop methode van de Applet wordt geactiveerd
{ for ( int count = 0; count < threads.length; count++ )
threads[ count ] = null; // zet references op null zodat de threads run methode te beëindigt
notifyAll(); // notify all waiting threads, so they can terminate
}
public synchronized void actionPerformed( ActionEvent event ) //verwerk button events
{ for ( int count = 0; count < checkboxes.length; count++ )
{ if ( event.getSource() == checkboxes[ count ] )
{ suspended[ count ] = !suspended[ count ]; //verander labelkleur bij suspend/resume
outputs[ count ].setBackground( suspended[ count ] ? Color.RED : Color.GREEN );
if ( !suspended[ count ] ) //bij resume: verzeker dat de thread tot executie overgaat
notifyAll();
return;
}
}
}
JAVA
65
10. RUNNABLE INTERFACE.
private class RunnableObject implements Runnable // inner klasse om de threads te besturen
{ public void run() // plaats willekeurig letters in GUI label
{ final Thread currentThread = Thread.currentThread(); //moeten final omdat inner class
final int index = getIndex( currentThread );
// ernaar refereert
while ( threads[ index ] == currentThread ) //deze voorwaarde kan de thread doen beëindigen
{ try
//dit gebeurt door thread[index] op null te zetten
{ Thread.sleep( ( int ) ( Math.random() * 1000 ) ); // sleep 0 tot 1 seconde
// determine whether thread should suspend execution;
synchronized( RandomCharacters.this ) //moet execution suspend worden
{ while ( suspended[ index ] && threads[ index ] == currentThread )
{ RandomCharacters.this.wait(); // tijdelijk suspend thread executie
}
} // einde synchronized block
}
catch ( InterruptedException exception ) { exception.printStackTrace(); }
JAVA
66
10. RUNNABLE INTERFACE.
SwingUtilities.invokeLater( new Runnable()
{ public void run() //kies willekeurig een character en toon in JLabel
{ char displayChar = alphabet.charAt( ( int ) ( Math.random() * 26 ) );
outputs[ index ].setText( currentThread.getName() + ": " + displayChar );
}
} ); // einde anonieme inner class
} // einde while methode run
System.err.println( currentThread.getName() + " terminating" );
} // einde method run
} // einde private inner class RunnableObject
} // einde class RandomCharacters
JAVA
67
Oefening: Responsible GUI.


Schrijf een GUI applicatie die de interactiviteit met de gebruiker
blijft verzorgen. Na het activeren van een taak zal niet gewacht
worden tot die eventeel tijdslopende taak voltooid is. De
applicatie kan onmiddellijk nieuwe verzoeken van de gebruiker
accepteren.
Voorzie een JFrame met drie Jbuttons (taak1, taak2, taak3) die
een taak activeren. Je kiest voor willekeurige slaaptijd van 0 tot
10 seconden om ‘het werk’ te simuleren.Een taak informeert
steeds in het JTextArea dat ze gestart is “Taak1 gestart”.
Wanneer de taak gaat eindigen zal ze de regel verder
aanpassen door de uitbreiding “... volbracht”.
JAVA
68
Oefening:
Responsible
GUI.
JAVA
69
OPLOSSING RESPONSIBLE GUI.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class ResponsiveGUI extends JFrame
{ private JButton btnTask1, btnTask2, btnTask3;
private JTextArea edOut;
private ActionListener buttonHandler;
public ResponsiveGUI()
{ super("Responsive GUI");
edOut = new JTextArea(10, 20);
edOut.setEditable(false);
buttonHandler = new ActionListener()
{ public void actionPerformed(ActionEvent e)
{ if (e.getSource()==btnTask1) { new Task1(edOut).start(); }
// enz … tot btnTaskn
}
};
JAVA
70
OPLOSSING RESPONSIBLE GUI.
btnTask1 = new JButton("Taak1");
// enz tot btnTaskn
btnTask1.addActionListener(buttonHandler);
// enz tot btnTaskn
Container c = getContentPane();
c.setLayout(new FlowLayout());
c.add(btnTask1);
// enz tot btnTaskn
c.add(new JScrollPane(edOut));
}
JAVA
71
OPLOSSING RESPONSIBLE GUI.
public static void main(String arg[])
{ ResponsiveGUI rpGUI = new ResponsiveGUI();
rpGUI.setSize(300,100);
rpGUI.setVisible(true);
rpGUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
class UpdateMessage implements Runnable
{ String mes;
JTextArea edOut;
public UpdateMessage(String m, JTextArea t) { mes=m; edOut = t; }
public void run()
{ StringBuffer buf = new StringBuffer(edOut.getText());
int i = buf.indexOf(mes);
edOut.setText(buf.replace(i+mes.length()-1, i+mes.length(),"... volbracht\n").toString());
}
}
JAVA
72
OPLOSSING RESPONSIBLE GUI.
class Task1 extends Thread
{ JTextArea edOut;
public Task1(JTextArea f)
{ edOut = f;
}
public void run()
{ String mes = "Taak1 gestart\n";
edOut.append(mes);
try
{ sleep((int)(Math.random()*10001));
}
catch(InterruptedException e) { throw new RuntimeException(e); }
SwingUtilities.invokeLater(new UpdateMessage(mes, edOut));
}
}
// enz … tot class Taskn
JAVA
73
Download