java1 h 16. multithreading. 1. inleiding. threads: delen van het programma die in concurrentie met...
TRANSCRIPT
JAVA 1
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 2
2. THREAD TOESTANDEN: LEVENSCYCLUS.
Ready
Running
BlockedSleepingWaiting
start
issue I/O
requestwait
notify
notifyAll
time
ou
t e
xpire
sinterrupt
thread dispatch(assign a processor)
quantum expirationyield
sleep
complete
sleep interval expires
interrupt
Born
enter synchronized
statement
I/O co
mp
lete
sa
cqu
ire lo
ckinterrupt
Bij voltooing van een thread (terugkeer van run methode), wordt de Dead toestand bereikt (hier de final state)
JAVA 3
2. THREAD TOESTANDEN: LEVENSCYCLUS.
Thread toestanden. Born state
Thread is juist gecreëerd. Ready state (ook Runnable state)
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 4
2. THREAD TOESTANDEN: LEVENSCYCLUS.
Blocked state Als een taak niet onmiddellijk kan volbracht
worden (vb. i/o verzoek). Waiting state
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 5
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 6
3. THREAD PRIORITIES EN SCHEDULING.
Priority 9
Priority 8
Priority 7
Priority 10
Priority 6
Priority 5
Priority 4
Priority 3
Priority 2
Priority 1
A B
D
C
E F
G
H I
J K
Ready threads
Thread.MIN_PRIORITY
Thread.MAX_PRIORITY
Thread.NORM_PRIORITY
JAVA 7
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 executieclass 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 8
// 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
4. CREATIE EN EXECUTIE VAN THREADS.
JAVA 9
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 10
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 threadsThreads started, main ends thread1 going to sleep for 1217thread2 going to sleep for 3989thread3 going to sleep for 662thread3 done sleepingthread1 done sleepingthread2 done sleeping
Starting threadsthread1 going to sleep for 314thread2 going to sleep for 1990Threads started, main ends thread3 going to sleep for 3016thread1 done sleepingthread2 done sleepingthread3 done sleeping
JAVA 11
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 12
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 13
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 14
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 15
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 16
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 17
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 18
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 19
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 20
6. PRODUCER/CONSUMER RELATIE ZONDER SYNCHRONISATIE.
Consumer reads -1Producer writes 1Consumer reads 1Consumer reads 1Consumer reads 1Consumer read values totaling: 2.Terminating Consumer.Producer writes 2Producer writes 3Producer writes 4Producer done producing.Terminating Producer.
Producer writes 1Producer writes 2Consumer reads 2Producer writes 3Consumer reads 3Producer writes 4Producer done producing.Terminating Producer.Consumer reads 4Consumer reads 4Consumer read values totaling: 13.Terminating Consumer.
JAVA 21
6. PRODUCER/CONSUMER RELATIE ZONDER SYNCHRONISATIE.
Producer writes 1Consumer reads 1Producer writes 2Consumer reads 2Producer writes 3Consumer reads 3Producer writes 4Producer done producing.Terminating Producer.Consumer reads 4Consumer read values totaling: 10.Terminating Consumer.
JAVA 22
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 23
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
} // einde methode set; beëindig blokkering op SynchronizedBuffer (release lock)
JAVA 24
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 op SynchronizedBuffer (release lock)
JAVA 25
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 26
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 27
7. PRODUCER/CONSUMER RELATIE MET SYNCHRONISATIE. Output 1.
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 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
JAVA 28
7. PRODUCER/CONSUMER RELATIE MET SYNCHRONISATIE. Output 1.
Consumer reads 3 3 0 Consumer tries to read.Buffer empty. Consumer waits. 3 0 Producer writes 4 4 1 Consumer reads 4 4 0Producer done producing.Terminating Producer. Consumer read values totaling: 10.Terminating Consumer.
Vervolg…
JAVA 29
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 2 1 Producer tries to write.Buffer full. Producer waits. 2 1 Consumer reads 2 2 0 Producer writes 3 3 1 Consumer reads 3 3 0
Producer writes 4 4 1
JAVA 30
7. PRODUCER/CONSUMER RELATIE MET SYNCHRONISATIE. Output 2.
Producer done producing.Terminating Producer.Consumer reads 4 4 0 Consumer read values totaling: 10.Terminating Consumer.
Vervolg…
JAVA 31
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 2 1Consumer reads 2 2 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 32
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 33
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 34
class Kelner extends Thread
{ 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);
}
}
}
OPLOSSING SYNCHRONISATIE: RESTAURANT
JAVA 35
class Kok extends Thread
{ private Restaurant 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); }
}
}
}
OPLOSSING SYNCHRONISATIE: RESTAURANT
JAVA 36
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();
}
OPLOSSING SYNCHRONISATIE: RESTAURANT
JAVA 37
public synchronized Order getOrder()
{ while (order==null)
{ try
{ wait();
}
catch(InterruptedException e) { throw new RuntimeException(e); }
}
Order ref=order;
order = null;
notifyAll();
return ref;
}
OPLOSSING SYNCHRONISATIE: RESTAURANT
JAVA 38
public static void main(String[] args)
{ Restaurant restaurant = new Restaurant();
new Kelner(restaurant, "Sofie");
new Kelner(restaurant, "Hendrik");
new Kok(restaurant);
}
}
OPLOSSING SYNCHRONISATIE: RESTAURANT
JAVA 39
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 40
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 41
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 42
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 43
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 44
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 45
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 46
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 47
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 48
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 49
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 50
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 51
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 52
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 53
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 54
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 55
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE BUFFER.
Waarde in laatste buffer geplaatst. De volgende waarde komt in de eerste buffer.
JAVA 56
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.
Circulair buffer effect—de zevende waarde wordt in de eerste buffer geplaatst.
JAVA 57
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE BUFFER.
Waarde in laatste buffer geplaatst. De volgende waarde komt in de eerste buffer.
Circulair buffer effect—de tiende waarde wordt in de eerste buffer geplaatst.
JAVA 58
8. PRODUCER/CONSUMER RELATIE: CIRCULAIRE BUFFER.
JAVA 59
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 60
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 61
10. RUNNABLE INTERFACE.
JAVA 62
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 63
10. RUNNABLE INTERFACE. public void init() //aanmaak GUI en arrays
{ outputs = new JLabel[ SIZE ]; checkboxes = new JCheckBox[ SIZE ];
threads = new Thread[ 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 64
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 65
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 66
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 67
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 68
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 69
Oefening:ResponsibleGUI.
JAVA 70
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 71
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 72
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()); }}
OPLOSSING RESPONSIBLE GUI.
JAVA 73
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