Beispiel: Kritischer Pfad
Beispiel: Kritischer PfadDas hier benutzte Beispielprogramm visualiert 15 Threads die sich auf einem synchronisierten Objekt serialisieren.
Threads in grüner Farbe befinden sich nicht im kritischen Pfad. Threads in roter Farbe befinden sich im kritischen Pfad.
Der kritische Pfad in in der Klasse EinMonitor in der Methode buchen() implementiert.
Die einzige Instanz von EinMonitor verfügt über zwei Variablen a und b die Konten darstellen sollen.
Die Methode buchen() "verschiebt" mehrfach einen Betrag zwischen den beiden Konten. Die Summe der beiden Variablen sollte am Ende der Methode stets gleich sein.
Die Methode buchen() enthält etwas Overhead zur Visualierung des Konzepts:
- am Anfang und am Ende muss das GUI Subsystemen über den Eintritt und das Verlassen des kritischen Pfads informiert werden
- zwischen allen Buchungen werden kleine Schlafpausen eingelegt um die Zeit im kritischen Pfad künstlich zu verlängern
Die Methode buchen() enthält eine Konsistenzprüfung am Ende der Methode die bei einem Fehler in der Buchführung eine Konsolenmeldung ausdruckt. Sie kommt in der auf dieser Seite gezeigten Variante nicht zum Zuge!
Die Klasse MainTest dient zum Starten des Programms. Sie erzeugt und startet die 15 Threads. Jeder Thread führt nur eine gewisse Anzahl von Buchungen durch und beendet sich dann.
Aufgaben
- Übersetzen Sie die Klassen und starten Sie das Hauptprogramm
- Der Schieberegler erlaubt das Einstellen der Schlafpausen im kritischen Pfad. Was geschieht wenn der kritische Pfad verkürzt wird?
- Entfernen die das Schlüsselwort synchronized in der Methode buchen(). Was geschieht?
- Was würde geschehen geschehen wenn die künstlichen sleep Aufrufe entfernt werden?
- Hinweis: Sie müssen dann auch die Anzahl der Durchläufe pro Thread stark erhöhen. Da die Zeit im kritischen Pfad sehr kurz wird.
- Was geschieht wenn man den yield() Aufruf für in der run() Methode von MainTest entfernt
Kommentar
Da die aktuelle Ausführungsgeschwindigkeit, 4-5 Zehnerpotenzen, jenseits der menschlichen Wahrnehmungsfähigkeit liegt ist, es sehr schwer die echten Abläufe im Zeitlupentempo zu visualisieren. Ein künstlicher sleep() Aufruf blockiert den Prozess und gibt den Prozessor an das Betriebssystem zurück. Der Scheduler des Betriebssystems trifft bei dieser künstlichen Verlangsamung eventuell andere Entscheidungen in Bezug auf den Thread den er ausführt. Das gleiche Problem besteht beim Debuggen von Javaprogrammen. Durch das Bremsen bestimmter Threads können existierende Fehler nicht mehr reproduzierbar sein oder bisher nicht aufgetretene Fehler in der Synchronsiation können sichtbar werden.
Starten des Programms
Die benötigten Klassen sind in Threading.jar zusammen gefaßt.
Man kann diese jar Datei mit
java -jar Threading.jar
von der Kommandozeile nach dem Runterladen starten. Vielleicht reicht auch ein Doppelklick auf die Datei im Download-Ordner...
Nach dem Übersetzen der Dateien oder nach dem Starten der jar-Datei erscheint ein GUI wie es im folgenden Bild zu sehen ist:
Klasse MainTest
Hauptprogramm der Anwendung.
package s2.thread; /** * * @author s@scalingbits.com */ public class MainTest extends Thread { public static final int INCRITICALPATH = 0; public static final int NOTINCRITICALPATH = 1; public static final int ENDED = 2; public static int anzahlThreads = 15; public static MainTest[] mt; public int threadStatus = NOTINCRITICALPATH; private static EinMonitor myMonitor; public static int sleepPeriod = 500; public int meineID; public static ThreadingPanel tp; public static ThreadFenster tg; public boolean stop = false; public boolean synchron = true; public MainTest(int id) { meineID = id; } @Override public void run() { long anfangszeit = System.nanoTime(); System.out.println("Thread [" + meineID + "] gestartet"); //GUIupdate(NOTINCRITICALPATH); for (long i = 0; i < 200; i++) { Thread t = Thread.currentThread(); // Erlaube anderen Threads die CPU zu holen t.yield(); if (tg.synchron) myMonitor.buchen(10); else myMonitor.parallelbuchen(10); } threadStatus = ENDED; System.out.println("Thread [" + meineID + "] beendet..."); } public static void main(String[] args) { // Anlegen des Monitorobjekts myMonitor = new EinMonitor(1000000L); mt = new MainTest[anzahlThreads]; tg = new ThreadFenster(); tp = tg.tp; // Erzeuge die Threads for (int i = 0; i < anzahlThreads; i++) { mt[i] = new MainTest(i); } // Starte die Threads for (int i = 0; i < anzahlThreads; i++) { mt[i].start(); } } }
Klasse EinMonitor
package s2.thread; /** * * @author s@scalingbits.com */ public class EinMonitor { long invariante; long a; long b; public EinMonitor(long para) { invariante = para; a = para; b = 0L; } synchronized public void buchen(long wert) { GUIupdate(MainTest.INCRITICALPATH); sleepABit(MainTest.sleepPeriod/5); this.a = this.a - wert; sleepABit(MainTest.sleepPeriod/5); this.b = this.b + wert; sleepABit(MainTest.sleepPeriod/5); this.a = this.a + wert; sleepABit(MainTest.sleepPeriod/5); this.b = this.b - wert; sleepABit(MainTest.sleepPeriod/5); GUIupdate(MainTest.NOTINCRITICALPATH); if ((a+b) != invariante) System.out.println("Inkonsistenter Zustand"); } public void parallelbuchen(long wert) { GUIupdate(MainTest.INCRITICALPATH); sleepABit(MainTest.sleepPeriod/5); this.a = this.a - wert; sleepABit(MainTest.sleepPeriod/5); this.b = this.b + wert; sleepABit(MainTest.sleepPeriod/5); this.a = this.a + wert; sleepABit(MainTest.sleepPeriod/5); this.b = this.b - wert; sleepABit(MainTest.sleepPeriod/5); GUIupdate(MainTest.NOTINCRITICALPATH); if ((a+b) != invariante) System.out.println("Inkonsistenter Zustand"); } private void sleepABit(int sleep) { try { Thread.sleep(sleep); } catch (InterruptedException e) {} } private void GUIupdate(int status) { MainTest t = (MainTest) Thread.currentThread(); t.threadStatus = status; t.tp.repaint(); } }
Klasse ThreadFenster
package s2.thread; import java.awt.BorderLayout; import java.awt.Container; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JSlider; import javax.swing.JTextField; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /** * * @author s@scalingbits.com * */ public class ThreadFenster { final private JFrame hf; private JButton okButton; final private JButton exitButton; JTextField threadDisplay; private final static int SLEEPMIN = 1; private final static int SLEEPMAX = 2000; private final static int SLEEPINIT = 500; private final int threadCurrent = 10; public ThreadingPanel tp; public boolean synchron = true; JRadioButton syncButton; JRadioButton nosyncButton; public class exitActionListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { System.exit(0); } } /** * Aufbau des Fensters zur Ausnahmebehandlung * */ public ThreadFenster() { JPanel buttonPanel; // Erzeugen einer neuen Instanz eines Swingfensters hf = new JFrame("Thread Monitor"); //Nicht Beenden bei Schliesen des Fenster hf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Anlegen der Buttons exitButton = new JButton("Beenden"); JLabel threadsLabel = new JLabel("sleep(ms):"); JSlider threadSlider = new JSlider (JSlider.HORIZONTAL, SLEEPMIN, SLEEPMAX, SLEEPINIT); threadDisplay = new JTextField(); threadDisplay.setText(Integer.toString(threadCurrent)); threadDisplay.setColumns(4); threadDisplay.setEditable(false); threadSlider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { JSlider source = (JSlider) e.getSource(); if (!source.getValueIsAdjusting()) { MainTest.sleepPeriod = source.getValue(); threadDisplay.setText(Integer.toString(MainTest.sleepPeriod)); } } }); exitButton.addActionListener(new exitActionListener()); syncButton = new JRadioButton("Synchronisiert"); syncButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { synchron= true; System.out.println("Synchronisiert"); } } ); syncButton.setSelected(true); nosyncButton = new JRadioButton(" Nicht Sync."); nosyncButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { synchron= false; System.out.println("Nicht synchronisiert"); } } ); ButtonGroup group = new ButtonGroup(); group.add(syncButton); group.add(nosyncButton); JPanel syncPanel = new JPanel(); BoxLayout bl = new BoxLayout(syncPanel, BoxLayout.Y_AXIS); syncPanel.setLayout(bl); syncPanel.add(syncButton); syncPanel.add(nosyncButton); //Aufbau des Panels //buttonPanel = new JPanel(new GridLayout(1, 0)); buttonPanel = new JPanel(); buttonPanel.add(threadsLabel); buttonPanel.add(threadSlider); buttonPanel.add(threadDisplay); //buttonPanel.add(okButton); buttonPanel.add(syncPanel); buttonPanel.add(exitButton); tp = new ThreadingPanel(); // Aubau des ContentPanes Container myPane = hf.getContentPane(); myPane.add(buttonPanel, BorderLayout.SOUTH); myPane.add(tp, BorderLayout.CENTER); JMenuBar jmb = new JMenuBar(); JMenu jm = new JMenu("Ablage"); jmb.add(jm); JMenuItem jmi = new JMenuItem("Beenden"); jmi.addActionListener(new exitActionListener()); jmi.setEnabled(true); jm.add(jmi); hf.setJMenuBar(jmb); //Das JFrame sichtbar machen hf.pack(); hf.setVisible(true); hf.setAlwaysOnTop(true); } }
Klasse ThreadingPanel
package s2.thread; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import javax.swing.JPanel; /** * * @author s@scalingbits.com */ public class ThreadingPanel extends JPanel { private final int ziffernBreite = 10; // Breite einer Ziffer in Pixel private final int ziffernHoehe = 20; // Hoehe einer Ziffer in Pixel public ThreadingPanel() { setPreferredSize(new Dimension(200, 100)); setDoubleBuffered(true); } /** * Methode die das Panel überlädt mit der Implementierung * der Treads * @param g */ @Override public void paintComponent(Graphics g) { super.paintComponent(g); int maxWidth = getWidth(); int maxHeight = getHeight(); g.setColor(Color.black); g.drawString("Anzahl threads: " + MainTest.anzahlThreads, 10, 20); for (int i = 0; i < MainTest.anzahlThreads; i++) { paintThread(g, i, 20 + 25 * i, 30); } } /** * Malen eines Threads und seines Zustands * @param g Graphicshandle * @param id Identifier * @param x X Koordinate des Thread * @param y Y Koordinate des Thread */ public void paintThread(Graphics g, int id, int x, int y) { int xOffset = 1; // offset Box zu Text int yOffset = 7; // offset Box zu Text //String wertThread = k.toString(); // Wert als Text if (MainTest.mt[id] != null) { switch(MainTest.mt[id].threadStatus) { case MainTest.ENDED: g.setColor(Color.LIGHT_GRAY); break; case MainTest.NOTINCRITICALPATH: g.setColor(Color.GREEN); break; case MainTest.INCRITICALPATH: g.setColor(Color.RED); break; default: assert(true):"Hier laeuft etwas falsch"; } } int breite = 2 * ziffernBreite; int xNextNodeOffset = 20; int yNextNodeOffset = ziffernHoehe * 6 / 5; // Vertikaler Offset zur naechsten Kn.ebene //g.setColor(Color.); // Farbe des Rechtecks im Hintergrund g.fillRoundRect(x - xOffset, y - yOffset, breite, ziffernHoehe, 3, 3); g.setColor(Color.black); // Schriftfarbe g.drawString(Integer.toString(id), x + xOffset, y + yOffset); } }
- 4788 views