Beispiellösung - Hochschule Niederrhein

Werbung
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2015
Prof. Dr. Nitsche
Übung 9 - Beispiellösung
Threads
In dieser Übung beschäftigen wir uns mit der Realisierung von Threads in Java.
Aufgabe 1: Erzeugen und Starten von Threads
a) Sei BankKunde eine von einer Klasse Kunde abgeleitete Klasse. Erweitern Sie die
Klasse BankKunde derart, dass Bankkunden nebenläufig als Java-Threads operieren
können.
Als Thread-Operation soll 50-mal „Jetzt arbeitet <Kunde>“ ausgegeben werden,
wobei <Kunde>der Name des Kunden ist.
b) Erzeugen und starten Sie zwei BankKunden-Threads dagobert und donald.
c) Analysieren Sie die Ausgabe des Programms.
Starten Sie dazu das Programm mehrmals hintereinander. Ist die Ausgabe jeweils
gleich, oder können Sie Unterschiede im Programmablauf erkennen?
Hinweis: Sie können die Ausgabe auch zu „Jetzt arbeitet <Kunde> zum <i>-ten Mal“
abändern, um evtl. stärkere Effekte zu beobachten.
Aufgabe 2: Nebenläufigkeit und Scheduling von Threads
a) Betrachten Sie die folgende Klasse Konto.
public class Konto {
private int kontostand = 50; // Anfangsstand 50 Euro
public int getKontoStand() {
return this.kontostand;
}
public void abbuchen(int betrag) {
int kontostand = this.kontostand;
this.kontostand = kontostand - betrag;
}
}
Analysieren Sie alle möglichen Abläufe, wenn 2 verschiedene Threads auf das gleiche
Konto zugreifen wollen (d.h. nebenläufig die Methode abbuchen aufrufen), und
jeweils 30 bzw. 40 Euro abheben wollen. Wie ist jeweils der Kontostand am Ende?
Konto konto = new Konto();
// Anfangs-Kontostand = 50
thread1: konto.abbuchen( 30 );
||
thread2: konto.abbuchen( 40 );
S. 1 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2013
thread1: konto.abbuchen( 30 );
||
Prof. Dr. Nitsche
Übung 9 – Beispiellösung
thread2: konto.abbuchen( 40 );
b) Nicht ausgelastet? Analysieren Sie analog alle möglichen Abläufe und den daraus
resultierenden Endbetrag des Kontostandes, wenn die Methode abbuchen wie
folgt abgeändert wird:
public void abbuchen(int betrag) {
if (this.kontostand >= betrag) {
int kontostand = this.kontostand;
this.kontostand = kontostand - betrag;
} else {
throw new IllegalArgumentException(
"Keine Kontoüberziehung erlaubt.");
}
}
thread1: konto.abbuchen( 30 );
||
thread2: konto.abbuchen( 40 );
c) Synchronisieren Sie die Aufrufe der Methode abbuchen durch das Schlüsselwort
synchronized.
Welche Abläufe und Kontoendstände sind hier möglich?
public synchronized void abbuchen(int betrag) {
if (this.kontostand >= betrag) {
int kontostand = this.kontostand;
this.kontostand = kontostand - betrag;
} else {
throw new IllegalArgumentException(
"Keine Kontoüberziehung erlaubt.");
}
}
thread1:konto.abbuchen( 30 );
||
thread2:konto.abbuchen( 40 );
S. 2 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2013
Prof. Dr. Nitsche
Übung 9 – Beispiellösung
Aufgabe 3: Tic TacToe
a) Stellen Sie das Spiel Tic TacToe fertig (vgl. Übung 8).
Hinweis: Die Ereignis-Bearbeitung können Sie analog zu der in Übung 8 realisieren,
die Spielobjekte (Kreise und Kreuze) können Sie aus den vorherigen Übungen
übernehmen (siehe Webseite der Veranstaltung).
•
•
Implementieren Sie eine Ereignis-Verarbeitung für die beiden Buttons.
Reagieren Sie auf ferner auf Mausklicks im Zeichenpanel.
b) Nicht ausgelastet? Erweitern Sie die Ereignis-Bearbeitung derart, dass bei Änderung
der Fenstergröße automatisch auch die Spielobjekte (Kreise und Kreuze) in ihrer
Größe angepasst werden.
Hinweis: Über die Änderung der Größe einer GUI-Komponente können Sie durch die
ComponentListener-Methode componentResized informieren lassen.
Zusatz-Aufgabe 4: Bewegung von Objekten auf dem Bildschirm: Pong
Das Spiel Pong gilt als das erste weltweit erfolgreiche Videospiel. Das Spielprinzip ähnelt
dem des Tischtennis: Ein Punkt („Ball“) bewegt sich auf dem Bildschirm hin und her. Jeder
der beiden Spieler steuert einen senkrechten Strich („Schläger“), den er mit einem
Drehknopf (Paddle) nach oben und unten verschieben kann. Lässt man den „Ball“ am
„Schläger“ vorbei, erhält der Gegner einen Punkt.[wikipedia.de]
S. 3 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
Bevor wir uns um das eigentliche Spielverhalten kümmern, sorgen wir zunächst für die
Bewegung auf dem Bildschirm: Bewegen Sie einen Ball innerhalb des Fensters. Der Ball soll
an den Rändern des Fensters „zurückprallen“ und dadurch ständig in Bewegung bleiben. (Im
realen Spiel gilt nur der obere und untere Rand als Bande und die Seiten als „Aus“.)
a) Erzeugen Sie zunächst eine Klasse PongPanel zum Zeichnen sowie eine
Hauptklasse Pong, in der Sie ein Fenster mit dem Panel darstellen.
b) Erzeugen Sie dann einen Ball, welcher das Interface
games.basic.gameObjects.interfaces.Moveable implementiert.
Hinweis: Ein Ball ist ein (farbig) ausgefüllter Kreis, der sich auf dem Bildschirm
bewegen kann. Bevor Sie also alles neu implementieren, schauen Sie doch einfach
nach, ob Sie da bereits bestehende Klassen (wieder-)verwenden können.
c) Setzen Sie den Ball in Bewegung. Rufen Sie dazu ca. alle 30 Millisekunden die moveMethode des Ball-Objektes auf. (Sie können natürlich auch mehrere Bälle einsetzen)
d) Ändern Sie nun die move-Methode derart, dass sich bei Berührung mit dem Rand des
Panels jeweils die x- bzw. y-Richtung der Bewegung ändert.
Hinweis: Dazu können Sie die Methoden reverseXDirection() bzw.
reverseYDirection() von Moveable verwenden, sowie die Testfunktionen
wie isBelowOf(y),isRightOf(x) etc. von GameObject.
S. 4 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
Alt (SS 2010):
package exercises.gui.pong;
import java.awt.Color;
import java.awt.Graphics;
import games.basic.gameObjects.moveable.AbstractMoveableGameObject;
public class Ball extends AbstractMoveableGameObject {
private int radius = 5;
public Ball(int x, int y) {
super(x, y);
this.setDirection(3, 1);
}
@Override
public void paintComponent(Graphics g) {
g.setColor(Color.RED);
g.fillOval(this.getX(), this.getY(), this.getWidth(), this.getHeight());
}
@Override
public int getWidth() {
return radius * 2;
}
@Override
public int getHeight() {
return radius * 2;
}
}
package exercises.gui.pong;
import java.awt.Color;
import java.awt.Graphics;
import games.basic.gameObjects.moveable.AbstractMoveableGameObject;
public class Paddle extends AbstractMoveableGameObject {
private int width = 10;
private int height = 30;
public Paddle(int x, int y) {
super(x, y);
this.setDirection(0, 2);
}
S. 5 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
@Override
public void paintComponent(Graphics g) {
g.setColor(Color.BLUE);
g.fillRect(this.getX(), this.getY(), this.getWidth(), this.getHeight());
}
@Override
public int getWidth() {
return width;
}
@Override
public int getHeight() {
return height;
}
}
package exercises.gui.pong;
import games.basic.gameObjects.interfaces.GameObject;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JPanel;
public class PongPanelSimple extends JPanel {
private PongSimple pong;
public PongPanelSimple(PongSimple pong) {
this.pong = pong;
this.setFocusable(true);
}
@Override
public void paintComponent(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0,0,getWidth(),getHeight());
for( GameObject gameObj : pong.gameObjects) {
gameObj.paintComponent(g);
}
//
//
//
//
g.setColor(Color.BLACK);
Font bigFont = new Font("serif", Font.BOLD, 30);
g.setFont(bigFont);
g.drawString(""+pong.leftPoints, 40, 40);
g.drawString(""+pong.rightPoints, getWidth()-80, 40);
S. 6 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
for (int i = 0; i < 10; i++) {
int lineLength = getHeight() / 20;
g.drawLine(getWidth() / 2, (2 * i) *lineLength,
getWidth()/2, (2 * i + 1)* lineLength);
}
}
}
package exercises.gui.pong;
import games.basic.gameObjects.interfaces.MoveableGameObject;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
public class PongSimple {
private JFrame frame;
PongPanelSimple pongPanel;
protected List<MoveableGameObject> gameObjects =
new ArrayList<MoveableGameObject>();
protected Ball ball = null;
protected Paddle leftPaddle;
protected Paddle rightPaddle;
protected int leftPoints = 0;
protected int rightPoints = 0;
public static void main(String args[]) {
PongSimple pong = new PongSimple();
pong.work();
}
private void work() {
frame = new JFrame("Pong");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pongPanel = new PongPanelSimple(this);
frame.add(pongPanel);
frame.setSize(400, 300);
frame.setVisible(true);
ball = new Ball(22, frame.getHeight() / 2);
gameObjects.add(ball);
S. 7 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
//
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
pongPanel.addKeyListener( new PongKeyListener() );
while( true ) {
try {
Thread.sleep(29);
} catch (InterruptedException e) { e.printStackTrace(); }
move();
frame.repaint();
}
}
private void move() {
System.out.println("frame: width = " + frame.getWidth() + ", height = " +
frame.getHeight());
System.out.println("panel: width = " + pongPanel.getWidth() + ", height = " +
pongPanel.getHeight());
System.out.println("ball: x = " + ball.getX() + ", y = " + ball.getY());
//
//
if (ball.isAboveOf(0) || ball.isBelowOf(pongPanel.getHeight())) { // falsch
if ((ball.getY() < 0) || (ball.getY() + ball.getHeight()) > (pongPanel.getHeight())) {
if (ball.touchesY(0) || ball.touchesY(pongPanel.getHeight())) {
ball.reverseYDirection();
//ball.move();
}
if (ball.isLeftOf(0) || ball.isRightOf(pongPanel.getWidth())) {
// falsch
if (ball.touchesX(0) || ball.touchesX(pongPanel.getWidth())) {
ball.reverseXDirection();
//ball.move();
}
//
ball.move();
}
}
package exercises.gui.pong;
import games.basic.gameObjects.interfaces.GameObject;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JPanel;
public class PongPanel extends JPanel {
private Pong pong;
S. 8 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
public PongPanel(Pong pong) {
this.pong = pong;
this.setFocusable(true);
}
@Override
public void paintComponent(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0,0,getWidth(),getHeight());
for( GameObject gameObj : pong.gameObjects) {
gameObj.paintComponent(g);
}
g.setColor(Color.BLACK);
Font bigFont = new Font("serif", Font.BOLD, 30);
g.setFont(bigFont);
g.drawString(""+pong.leftPoints, 40, 40);
g.drawString(""+pong.rightPoints, getWidth()-80, 40);
for (int i = 0; i < 10; i++) {
int lineLength = getHeight() / 20;
g.drawLine(getWidth() / 2, (2 * i) *lineLength,
getWidth()/2, (2 * i + 1)* lineLength);
}
}
}
package exercises.gui.pong;
import games.basic.gameObjects.interfaces.MoveableGameObject;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
public class Pong {
private JFrame frame;
private PongPanel pongPanel;
enum Player{left,right};
S. 9 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
Player nextPlayer = Player.left;
final char leftPlayerKey = 'a';
final char rightPlayerKey = 'l';
final char newBallKey = ' ';
protected int leftPoints = 0;
protected int rightPoints = 0;
protected List<MoveableGameObject> gameObjects =
new ArrayList<MoveableGameObject>();
private List<MoveableGameObject> paddles =
new ArrayList<MoveableGameObject>();
protected Ball ball = null;
protected Paddle leftPaddle;
protected Paddle rightPaddle;
public static void main(String args[]) {
Pong pong = new Pong();
pong.work();
}
private void work() {
frame = new JFrame("Pong");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pongPanel = new PongPanel(this);
frame.add(pongPanel);
frame.setSize(400, 300);
frame.setVisible(true);
leftPaddle = new Paddle(10, frame.getHeight() / 2);
rightPaddle = new Paddle(frame.getWidth() - 20, frame.getHeight() / 2);
gameObjects.add(leftPaddle);
gameObjects.add(rightPaddle);
paddles.add(leftPaddle);
paddles.add(rightPaddle);
pongPanel.addKeyListener( new PongKeyListener() );
while( true ) {
try {
Thread.sleep(29);
} catch (InterruptedException e) { e.printStackTrace(); }
move();
frame.repaint();
}
S. 10 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
}
private void move() {
rightPaddle.setX(pongPanel.getWidth()-20);
//
falsch
if (ball != null) {
if (ball.isAboveOf(0) || ball.isBelowOf(pongPanel.getHeight())) {
//
if (ball.touchesY(0) || ball.touchesY(pongPanel.getHeight())) {
ball.reverseYDirection();
ball.move();
}
if (ball.isLeftOf(0)) {
// Ball vollständig verschwunden
if (ball.touchesX(0)) {
// Ball berührt nur Wand
gameObjects.remove(ball);
ball = null;
rightPoints++;
nextPlayer = Player.right;
} else if (ball.isRightOf(pongPanel.getWidth())) { // Ball vollständig
verschwunden
//
Wand
} else if (ball.touchesX(pongPanel.getWidth())) {
//
// Ball berührt nur
gameObjects.remove(ball);
ball = null;
leftPoints++;
nextPlayer = Player.left;
} else if
//
//
//
rightPaddle.getHeight()))
//
//
//
leftPaddle.getHeight()))) {
(ball.touches(rightPaddle) || ball.touches(leftPaddle)) {
((ball.isRightOf( pongPanel.getWidth()-20)
&& ball.isBelowOf(rightPaddle.getY())
&& ball.isAboveOf(rightPaddle.getY() +
|| (ball.isLeftOf(20)
&& ball.isBelowOf(leftPaddle.getY())
&& ball.isAboveOf(leftPaddle.getY() +
ball.reverseXDirection();
}
}
//
//
for (MoveableGameObject gameObj : gameObjects) {
gameObj.move();
}
for (MoveableGameObject paddle : paddles) {
if (paddle.isAboveOf(0))
// falsch
if (paddle.touchesY(0))
paddle.setY(0);
if (paddle.isBelowOf( pongPanel.getHeight() - paddle.getHeight() ))
S. 11 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
if (paddle.touchesY( pongPanel.getHeight() ))
paddle.setY( pongPanel.getHeight() - paddle.getHeight() );
}
}
private class PongKeyListener implements KeyListener {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyChar() == leftPlayerKey) {
leftPaddle.setYDirectionUp();
}
if (e.getKeyChar() == rightPlayerKey){
rightPaddle.setYDirectionUp();
}
}
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyChar() == leftPlayerKey) {
leftPaddle.setYDirectionDown();
}
if (e.getKeyChar() == rightPlayerKey){
rightPaddle.setYDirectionDown();
}
}
@Override
public void keyTyped(KeyEvent e) {
if (ball == null && e.getKeyChar() == newBallKey) {
switch (nextPlayer) {
case right:
ball = new Ball(pongPanel.getWidth() - 32, rightPaddle.getY()
+ 10);
ball.reverseXDirection();
break;
case left:
ball = new Ball(22, leftPaddle.getY() + 10);
break;
}
gameObjects.add(ball);
}
}
} // class PongKeyListener
}
S. 12 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
Alt – SS 2010 (TicTacToe)
package exercises.gui.ttt;
import java.awt.Color;
import games.basic.gameObjects.CircularGameObject;
public class Kreis extends CircularGameObject {
private int feldX;
private int feldY;
private int breite;
private int hoehe;
public Kreis(int feldX, int feldY, int breite, int hoehe) {
super(feldX * breite, feldY * hoehe,
Math.min(breite, hoehe) / 2, Color.blue, 2);
//
System.out.println("Kreis: feldX = "+ feldX + ", feldY = " + feldY + ", breite = " + breite
+ ", hoehe = " + hoehe);
this.feldX = feldX;
this.feldY = feldY;
this.breite = breite;
this.hoehe = hoehe;
}
public void resize(int breite, int hoehe) {
this.breite = breite;
this.hoehe = hoehe;
this.setRadius(Math.min(breite, hoehe) / 2);
this.setPos(feldX * breite, feldY * hoehe);
}
}
package exercises.gui.ttt;
import java.awt.Color;
import games.basic.gameObjects.CrossGameObject;
public class Kreuz extends CrossGameObject {
private int feldX;
private int feldY;
private int breite;
private int hoehe;
public Kreuz(int feldX, int feldY, int breite, int hoehe) {
super(feldX * breite, feldY * hoehe,
breite, hoehe, Color.red, 2);
S. 13 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
//
System.out.println("Kreuz: feldX = "+ feldX + ", feldY = " + feldY + ", breite = " + breite
+ ", hoehe = " + hoehe);
this.feldX = feldX;
this.feldY = feldY;
this.breite = breite;
this.hoehe = hoehe;
}
public void resize(int breite, int hoehe) {
this.breite = breite;
this.hoehe = hoehe;
this.setWidth(breite);
this.setHeight(hoehe);
this.setPos(feldX * breite, feldY * hoehe);
}
}
package exercises.gui.ttt;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
import java.awt.Graphics;
import java.awt.Color;
import java.util.List;
import java.util.LinkedList;
import games.basic.gameObjects.interfaces.GameObject;
public class TicPanel extends JPanel {
private List<GameObject> gameObjects =
new LinkedList<GameObject>();
public void addGameObject(GameObject gameObj) {
gameObjects.add(gameObj);
}
public void clearGameObjects() {
gameObjects.clear();
}
@Override
public void paintComponent(Graphics g) {
int breite = this.getWidth();
int hoehe = this.getHeight();
g.setColor(Color.blue);
S. 14 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
g.drawLine(0, hoehe/3, breite, hoehe/3);
g.drawLine(0, hoehe/3*2, breite, hoehe/3*2);
g.drawLine(breite/3, 0, breite/3, hoehe);
g.drawLine(breite/3*2, 0, breite/3*2, hoehe);
g.drawRect(0, 0, breite-1, hoehe-1);
//this.setBorder( new LineBorder(Color.blue));
for( GameObject gameObj : gameObjects) {
if (gameObj instanceof Kreuz) {
((Kreuz) gameObj).resize(breite / 3,
hoehe / 3);
}
if (gameObj instanceof Kreis) {
((Kreis) gameObj).resize(breite / 3,
hoehe / 3);
}
gameObj.paintComponent(g);
}
}
}
package exercises.gui.ttt;
import games.basic.gameObjects.interfaces.GameObject;
import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class Tic implements ActionListener {
public static void main(String[] args) {
Tic tic = new Tic();
tic.run();
}
private JFrame frame;
private JButton newButton;
private JButton endeButton;
private TicPanel ticPanel;
TicMouseListener ticMouseListener;
S. 15 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
private boolean isKreuz = true;
private Spieler winner = Spieler.LEER; // currently no winner
enum Spieler { LEER, KREUZ, KREIS };
Spieler spielFeld[][] = new Spieler[3][3];
private void newGame() {
for (int feldX = 0; feldX < 3; feldX++) {
for (int feldY = 0; feldY < 3; feldY++) {
spielFeld[feldX][feldY] = Spieler.LEER;
}
}
ticPanel.clearGameObjects();
ticPanel.addMouseListener( ticMouseListener );
isKreuz = true;
frame.repaint();
}
private boolean isEnd() {
// Prüfe auf Spielende
for (int zeile = 0; zeile < 3; zeile++) {
if (spielFeld[zeile][0] == spielFeld[zeile][1]
&& spielFeld[zeile][0] == spielFeld[zeile][2]
&& spielFeld[zeile][0] != Spieler.LEER) {
winner = spielFeld[zeile][0];
// Gewinner
return true;
}
}
for (int spalte = 0; spalte < 3; spalte++) {
if (spielFeld[0][spalte] == spielFeld[1][spalte]
&& spielFeld[0][spalte] == spielFeld[2][spalte]
&& spielFeld[0][spalte] != Spieler.LEER) {
winner = spielFeld[0][spalte]; // Gewinner
return true;
}
}
// Diagonalen
if (spielFeld[0][0] == spielFeld[1][1]
&& spielFeld[0][0] == spielFeld[2][2]
&& spielFeld[0][0] != Spieler.LEER) {
winner = spielFeld[0][0];
// Gewinner
return true;
}
if (spielFeld[0][2] == spielFeld[1][1]
&& spielFeld[0][2] == spielFeld[2][0]
&& spielFeld[0][2] != Spieler.LEER) {
winner = spielFeld[0][2];
// Gewinner
return true;
}
// Prüfe auf unentscheiden
S. 16 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
winner = Spieler.LEER;
for (int feldX = 0; feldX < 3; feldX++) {
for (int feldY = 0; feldY < 3; feldY++) {
if (spielFeld[feldX][feldY] == Spieler.LEER)
return false;
// Spiel noch nicht zuende
}
}
return true;
// Spiel zuende, alle Felder belegt
}
private int getFeldBreite() {
return ticPanel.getWidth() / 3;
}
private int getFeldHoehe() {
return ticPanel.getHeight() / 3;
}
public void run() {
frame = new JFrame("Tic Tac Toe");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
newButton = new JButton("Neues Spiel");
endeButton = new JButton("Ende");
newButton.addActionListener(this);
endeButton.addActionListener(this);
JPanel buttonPanel = new JPanel();
ticPanel = new TicPanel();
ticPanel.setBackground( Color.darkGray);
ticMouseListener = new TicMouseListener();
ticPanel.addMouseListener( ticMouseListener );
//
// in newGame()
newGame();
frame.add( BorderLayout.CENTER, ticPanel);
frame.add( BorderLayout.SOUTH, buttonPanel);
buttonPanel.add(newButton);
buttonPanel.add(endeButton);
frame.setVisible(true);
frame.setSize(400, 400);
frame.repaint();
}
//
// --- ActionListener ---------------------------------------------------@Override
public void actionPerformed(ActionEvent e) {
System.out.println("actionPerformed: "
S. 17 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
//
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
+ e.getActionCommand());
if (e.getSource() == newButton) {
System.out.println("Neues Spiel starte ...");
this.newGame();
} else if (e.getSource() == endeButton){
System.out.println("Ende des Spiels ...");
System.exit(0);
}
}
private int getBreite() {
return ticPanel.getWidth();
}
// --- MouseListener ---------------------------------------------------private class TicMouseListener implements MouseListener {
//
//
//
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("mouseClicked");
int x = e.getX();
int y = e.getY();
System.out.println("Mausklick: x = " + x + ", y = " + y);
int breite = ticPanel.getWidth();
int hoehe = ticPanel.getHeight();
int feldX = x / (breite/3);
int feldY = y / (hoehe/3);
System.out.println("Feld: x = " + feldX + ", y = " + feldY);
if (spielFeld[feldX][feldY] == Spieler.LEER) {
// nur, falls Feld noch nicht belegt
GameObject temp;
if(isKreuz) {
temp = new Kreuz(feldX, feldY, breite / 3, hoehe / 3);
spielFeld[feldX][feldY] = Spieler.KREUZ;
isKreuz = false;
} else {
temp = new Kreis(feldX, feldY, breite / 3, hoehe / 3);
spielFeld[feldX][feldY] = Spieler.KREIS;
isKreuz = true;
}
ticPanel.addGameObject(temp);
ticPanel.repaint();
if (isEnd()) {
String msg = "Spiel beendet. ";
if (winner == Spieler.LEER)
msg += "Unentschieden";
else
S. 18 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
msg+= "Gewinner = " + winner;
System.out.println(msg);
JOptionPane.showMessageDialog(frame, msg, "Ende",
JOptionPane.INFORMATION_MESSAGE);
// verhindere weitere Eingaben, bis neues Spiel startet
ticPanel.removeMouseListener(ticMouseListener);
}
} else {
System.out.println("Feld bereits belegt!");
}
}
@Override
public void mouseEntered(MouseEvent e) {
System.out.println("mouseEntered");
}
//
@Override
public void mouseExited(MouseEvent e) {
System.out.println("mouseExited");
}
//
@Override
public void mousePressed(MouseEvent e) {
System.out.println("mousePressed");
}
//
@Override
public void mouseReleased(MouseEvent e) {
System.out.println("mouseReleased");
}
//
}
}
S. 19 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
Zusatz-Aufgabe 5: Synchronisation: Speisende Philosophen
Es sitzen 5 Philosophen an einem runden Tisch. Zwischen zwei Philosophen liegt jeweils eine
Gabel. Zum Essen benötigt jeder Philosoph zwei Gabeln. Offenkundig kann jede Gabel stets
nur von höchstens einem Philosoph in der Hand gehalten werden. Ein Philosoph muss also
ggf. warten, bis seine beiden Gabeln frei sind.
a) Erstellen Sie eine Klasse Gabel. Diese soll – wie eine Semaphore – zwei Methoden
void nehmen(Thread owner) und void freigeben() bereitstellen.
Denken Sie daran, dass jede Gabel stets nur von einem Thread besessen werden
kann, andere Threads beim Aufruf von nehmen folglich warten müssen, bis das
Gabel-Objekt wieder freigegeben ist.
b) Erstellen Sie ferner eine Klasse Philosoph, die als Thread operieren soll.
Übergeben Sie den Philosophen Referenzen auf deren linke bzw. rechte Gabel und
setzen Sie einen Namen für jeden Thread (z.B. „Philosoph 3“).
Jeder Philosoph nimmt Sie sich seine linke Gabel, seine rechte Gabel, „speist“ 3
Sekunden, und gibt dann die Gabeln wieder frei. Geben Sie vor und nach jeder Aktion
eine entsprechende Nachricht auf dem Bildschirm aus (jeweils mit Angabe des
Namens des Philosophen).
c) Erstellen Sie im Hauptprogramm 5 Gabel-Objekte und 5 Philosophen und
starten Sie die Philosophen-Threads.
Analysieren Sie den Ablauf des Programms.
d) Ändern Sie die Klasse Philosoph derart ab, dass Sie zwischen der Aufnahme der
linken und der Aufnahme der rechten Gabel jeweils eine Sekunde warten.
Inwieweit ändert sich dadurch der Ablauf ihres Programms?
e) Erweitern Sie ihr (Haupt-)programm derart, dass Sie durch Eingabe einer Zahl das
entsprechenden Philosophen-Objekt unterbrechen können, d.h. dessen
interrupt()-Methode aufrufen. Ein unterbrochener Thread soll evtl. belegte
Gabeln freigeben und sich dann beenden.
f) Nicht ausgelastet? Wie könnten Sie Ihr Programm abändern, sodass keine
Verklemmungen (Deadlocks) auftreten können? Hinweis: Suchen Sie – z.B. in der
Literatur – zunächst einen verklemmungsfreien Algorithmus, bevor Sie ihn in Java
realisieren.
S. 20 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
Philosopher0:
package exercises.threads.philosopher0;
public class Gabel {
private boolean belegt = false;
public boolean isBelegt() {
return belegt;
}
// request
public synchronized void nehmen() {
while (belegt) {
try {
wait();
} catch (InterruptedException e) {
System.out.println("Gabel.nehmen:
InterruptedException " + e.getMessage());
e.printStackTrace();
}
} // while
belegt = true;
}
// release
public synchronized void freigeben() {
belegt = false;
notify();
}
}
package exercises.threads.philosopher0;
public class Philosoph extends Thread {
private int number;
private Gabel links;
private Gabel rechts;
private boolean hatLinkeGabel = false;
private boolean hatRechteGabel = false;
public Philosoph(int number, Gabel links, Gabel rechts) {
this.number = number;
this.links = links;
this.rechts = rechts;
}
private void sleep(int ms) {
if (!this.isInterrupted()) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
System.out.println("Philosoph.sleep:
InterruptedException " + e.getMessage());
S. 21 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
this.interrupt();
}
}
}
public void run() {
System.out.println("Gestartet: " + this);
// nimm linke Gabel
while (this.links.isBelegt() && !this.isInterrupted()) {
System.out.println(this + " wartet auf linke Gabel");
sleep(5000);
}
if (!this.links.isBelegt() && !this.isInterrupted()) {
this.links.nehmen();
this.hatLinkeGabel = true;
}
System.out.println(this);
// nimm rechte Gabel
sleep(1000);
// --> sleep bewirkt deadlock
while (this.rechts.isBelegt() && !this.isInterrupted()) {
System.out.println(this + " und wartet auf rechte
Gabel");
sleep(5000);
}
if (!this.rechts.isBelegt() && !this.isInterrupted()) {
this.rechts.nehmen();
this.hatRechteGabel = true;
}
System.out.println(this);
// essen
sleep(1000);
sleep(35000);
System.out.println(this + "--> ist nun satt und gibt Gabeln
//
frei");
// Gabeln freigeben
this.rechts.freigeben();
this.hatRechteGabel = false;
System.out.println(this);
this.links.freigeben();
this.hatLinkeGabel = false;
System.out.println("Beendet: " + this);
}
@Override
public String toString() {
String val = "Philosoph " + number;
if (this.hatLinkeGabel) {
val += " hat linke Gabel";
}
if (this.hatRechteGabel) {
val += " hat rechte Gabel";
}
if (hatLinkeGabel && hatRechteGabel) {
S. 22 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
val += " und isst";
}
return val;
}
}
package exercises.threads.philosopher0;
import java.util.Scanner;
public class Main {
private static final int N = 5;
public static void main(String[] args) {
Gabel[] gabeln = new Gabel[N];
for (int i = 0; i < N; i++) {
gabeln[i] = new Gabel();
}
Philosoph[] phils = new Philosoph[N];
for (int i = 0; i < N; i++) {
phils[i] = new Philosoph(i, gabeln[i], gabeln[(i+1) %
N]);
phils[i].start();
}
while (true) {
Scanner scan = new Scanner(System.in);
int i = scan.nextInt();
phils[i].interrupt();
synchronized (phils[i]) {
phils[i].notify();
}
}
}
}
Philosopher2:
package exercises.threads.philosopher2;
public class Gabel {
private Thread owner = null;
public boolean isBelegt() {
return owner != null;
}
public Thread getOwner() {
return this.owner;
}
// request
public synchronized void nehmen(Thread owner) {
S. 23 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
while (this.isBelegt() && !owner.isInterrupted()) {
try {
wait();
} catch (InterruptedException e) {
System.out.println("Gabel.nehmen:
InterruptedException " + e.getMessage());
e.printStackTrace();
owner.interrupt();
}
} // while
//
belegt = true;
this.owner = owner;
}
//
// release
public synchronized void freigeben() {
belegt = false;
owner = null;
notify();
}
}
package exercises.threads.philosopher2;
public class Philosoph extends Thread {
private Gabel linkeGabel;
private Gabel rechteGabel;
public Philosoph(int number, Gabel linkeGabel, Gabel rechteGabel) {
this.setName("Philosoph " + number);
this.linkeGabel = linkeGabel;
this.rechteGabel = rechteGabel;
}
private void sleep(int ms) {
if (!this.isInterrupted()) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
System.out.println("Philosoph2.sleep:
InterruptedException " + e.getMessage());
this.interrupt();
}
}
}
public void run() {
System.out.println("Gestartet: " + this);
// nimm linke Gabel
System.out.println(this + " wartet auf linke Gabel");
if (!this.isInterrupted()) {
this.linkeGabel.nehmen(this);
}
System.out.println(this);
// nimm rechte Gabel
sleep(1000);
// --> sleep bewirkt deadlock
S. 24 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
System.out.println(this + " und wartet auf rechte Gabel");
if (!this.isInterrupted()) {
this.rechteGabel.nehmen(this);
}
System.out.println(this);
// essen
sleep(1000);
sleep(5000);
System.out.println(this + "--> ist nun satt und gibt Gabeln
//
frei");
// Gabeln freigeben
System.out.println(this + " und gibt rechte Gabel frei");
this.rechteGabel.freigeben();
System.out.println(this);
System.out.println(this + " gibt linke Gabel frei");
this.linkeGabel.freigeben();
System.out.println("Beendet: " + this);
}
@Override
public String toString() {
String val = this.getName();
if (this.hatGabel(linkeGabel)) {
val += " hat linke Gabel";
}
if (this.hatGabel(rechteGabel)) {
val += " hat rechte Gabel";
}
if (hatGabel(linkeGabel) && hatGabel(rechteGabel)) {
val += " und isst";
}
return val;
}
private boolean hatGabel(Gabel g) {
return g.isBelegt() && g.getOwner() == this;
}
}
S. 25 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
Zusatz-Aufgabe 6 (für Java-Experten): Java-Rätsel
a) Das folgende Programm iteriert durch eine Liste von int-Arrays und ermittelt, wie
viele der Arrays eine bestimmte Eigenschaft aufweisen. Was gibt das Programm aus?
public class Loop {
public static void main(String[] args) {
int[][] tests = { { 6, 5, 4, 3, 2, 1 }, { 1, 2 },
{ 1, 2, 3 }, { 1, 2, 3, 4 }, { 1 } };
int successCount = 0;
try {
int i = 0;
while (true) {
if (thirdElementIsThree(tests[i++]))
successCount++;
}
} catch (ArrayIndexOutOfBoundsException e) {
// No more tests to process
}
System.out.println(successCount);
}
private static boolean thirdElementIsThree(int[] a) {
return a.length >= 3 & a[2] == 3;
}
}
Ausgabe:
0
Lösung:
•
•
•
Wir erwarten, dass die Ausgabe 2 sein sollte: Es gibt genau zwei Teil-Arrays, die als 3.
Element eine 3 besitzen ({1, 2, 3} und {1, 2, 3, 4}).
Allerdings erhalten wir eine 0. Warum?
Zunächst sollte die Iteration über alle Array-Elemente nicht durch eine Exception
(ArrayIndexOutOfBoundsException) beendet werden, sondern als normale
Schleife definiert werden. (Ist vom Code besser verständlich, und auch deutlich
schneller).
for (int i = 0; i < tests.length; i++) {
if (thirdElementIsThree(tests[i++]))
successCount++;
}
...
private static boolean thirdElementIsThree(int[] a) {
return a.length >= 3 & a[2] == 3;
}
S. 26 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
•
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
Dann sehen wir, dass es plötzlich eine Exception gibt, die vorher „versteckt“ mit
abgefangen wurde:
Exception in thread "main"
java.lang.ArrayIndexOutOfBoundsException: 2
at
Loop_Korrektur_v1.thirdElementIsThree(Loop_Korrektur_v1.java:16
)
at Loop_Korrektur_v1.main(Loop_Korrektur_v1.java:9)
•
•
•
Grund ist der Vergleich in der Methode thirdElementIsThree: Der Operator & ergibt
eine bitweise und-Verknüpfung, und berechnet dazu beide Argumente. Folglich wird
auch dann auf a[2] zurückgegriffen, wenn das int-Array weniger als 3 Elemente
enthält (bei {1, 2} und {1} ).
Daran erkennen wir auch, warum unser Ausgangsprogramm als Ergebnis eine 0
ausgegeben hat: Das Array {6,5,4,3,2,1} hat keine 3 an 3. Stelle (d.h. successCount
bleibt 0), und beim nächsten Array {1,2} wird bereits die Schleife wegen einer
ArrayIndexOutOfBoundsException beendet.
Korrigieren wir in der Methode thirdElementIsThree den Operator zum logischen
Und-Operator &&, erhalten wir das Gewünschte. Dieser Operator wird „lazy“
ausgewertet, d.h. das zweite Argument wird nur betrachtet, wenn das erste
Argument true ist, d.h. das Array mindestens 3 Elemente umfasst und der Zugriff auf
a[2] damit nicht fehlschlägt.
private static boolean thirdElementIsThree(int[] a) {
return a.length >= 3 && a[2] == 3;
}
•
Allerdings gibt es jetzt die Ausgabe:
1
•
•
Grund ist die doppelte Erhöhung von i in der Schleife, d.h. wir Testen lediglich die
Arrays {6, 5, 4, 3, 2, 1}, {1, 2, 3} und {1}.
Dies korrigieren wir wie folgt:
for (int i = 0; i < tests.length; i++) {
if (thirdElementIsThree(tests[i]))
successCount++;
}
•
Das korrekte Programm liefert nun – wie erwartet:
2
S. 27 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
•
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
Anmerkung: Statt in der for-Schleife auf alle Array-Elemente per Index zuzugreifen,
können wir ab Java 5 auch die for-each-Schleife verwenden, die für alle
Datenstrukturen mit einem Iterator funktioniert:
for (int[] test : tests) {
if (thirdElementIsThree(test))
successCount++;
}
b) Dieses Programm berechnet und speichert eine Summe in einer Klasse und gibt sie in
einer anderen Klasse aus. Was gibt das Programm aus?
Hinweis: Die Summe aller Zahlen von 1 bis n beträgt n(n+1)/2.
class Cache {
static {
initializeIfNecessary();
}
private static int sum;
public static int getSum() {
initializeIfNecessary();
return sum;
}
private static boolean initialized = false;
private static synchronized void initializeIfNecessary()
{
if (!initialized) {
for (int i = 0; i < 100; i++) {
sum += i;
}
initialized = true;
}
}
}
public class Client {
public static void main(String[] args) {
System.out.println(Cache.getSum());
}
}
Ausgabe:
9900
Erklärung:
• Summe wäre eigentlich 100*99/2 = 4950.
S. 28 / 29
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
•
Grundlagen der
Java Programmierung
SS 2012
Prof. Dr. Nitsche
Übung 8 - Beispiellösung
Aber die Summe wird zweimal berechnet.
class Cache_Lsg {
private static final int SUM = computeSum();
private static int computeSum() {
int result = 0;
for (int i = 0; i < 100; i++) {
result += i;
}
return result;
}
public static int getSum() {
return SUM;
}
}
public class Client_Lsg {
public static void main(String[] args) {
System.out.println(Cache_Lsg.getSum());
}
}
c) Manchmal ist es nützlich zu zählen, wie viele Instanzen von einer Klasse erzeugt
wurden. Dies wird typischerweise durch eine statische, private Zählervariable
realisiert, die im Konstruktor inkrementiert wird. Im folgenden Programm
demonstriert die Creature-Klasse dieses Prinzip, während die Creator-Klasse die
Anzahl der erzeugten Creature-Instanzen ausgibt.
Was gibt das Programm aus?
public class Creator {
public static void main(String[] args) {
for (int i = 0; i < 100; i++)
Creature creature = new Creature();
System.out.println(Creature.numCreated());
}
}
class Creature {
private static long numCreated = 0;
public Creature() {
numCreated++;
}
public static long numCreated() {
return numCreated;
}
}
S. 29 / 29
Herunterladen