Download presentation
Presentation is loading. Please wait.
Published byDwayne Gallagher Modified over 7 years ago
1
Five-Minute Review What are the three data structures realized in the Java Collections Framework? What are checked exceptions? Example? What do iterators do? What are interactive programs? What types of events do we distinguish? Lists, Sets, Maps Exceptions that must be caught (try-catch) or propagated (throws). Example: IOException. Iterate through elements of collections, methods are hasNext() and next() Event-driven programs Mouse / keyboard / action events
2
Programming – Lecture 14 Looking Ahead (Chapter 14)
Programming patterns, MVC Concurrency Race Conditions Randomness in games
3
Programming Patterns In 1994, Addison-Wesley published Design Patterns, a groundbreaking book that called attention to the fact that many programming problems are most easily solved by applying certain common software patterns. Although the patterns described in the book are typically implemented using classes and methods, each pattern tends to represent more of a general solution strategy for some class of problems and not a ready-made solution.
4
The Model/View/Controller Pattern
The user interacts with a MVC-based user interface through the controller, which is the part of the system capable of accepting user commands. It is typically a collection of Swing interactors. The controller never updates the displayed data directly. It instead sends requests to the model, which keeps track of the state of the system but is separate from the user interface. One of the most important patterns in terms of its applicability to user-interface design is the model/view/controller pattern, which is often abbreviated as MVC. When changes occur in the model, it sends messages to one or more views, which are responsible for updating the information that the user sees on the display. Controller Model View
5
The HousePoints Program
The HousePoints program described in section 14.4 uses the model/view/controller pattern to present two views of the house points assigned to the Hogwarts houses at the end of J. K. Rowling’s Harry Potter and the Philosopher’s Stone. The first appears as a bar graph, the second as a pie chart. In this example, the role of the controller is taken by the interactors at the bottom of the window. The controller sends messages to the model, which in turn updates both views. HousePoints Points 48, 352, 426, 472 4, 352, 426, 472 312, 352, 426, 472 312, 352, 426, 472 482, 352, 426, 472 Graph
6
Model – View – Controller
HousePoints Points 48, 352, 426, 472 482, 352, 426, 472 312, 352, 426, 472 4, 352, 426, 472 312, 352, 426, 472 Graph
7
Concurrency One of the initial design criteria for Java was that it be able to support concurrency, which is simply the ability to carry on several activities in parallel, even on computers that have only one processor. The classical approach to supporting concurrency is called multiprogramming, in which a computer with a single processor runs multiple programs by sharing that processor among each of the individual processes. The computer runs one process for a short period and then switches over to one of the other processes, cycling around the different tasks to ensure that they all get a fair share of processor time. Java supports concurrency at a lower level by allowing users to create new threads, which are independent activities that coexist when the same program and share access to the same memory space. As in multiprogramming, a multithreaded system shares the processor among the active threads.
8
A Simple Concurrent Application
The TestAnimatedSquare program on the next slide offers a simple illustration of multithreaded programming. Each of the squares shown in the sample run below is an instance of the AnimatedSquare class, which implements the Runnable interface which allows it to support a thread. The run method for each square causes it to move in a random direction, which changes every 50 steps. TestAnimatedSquare
9
Concurrency TestAnimatedSquare
10
The TestAnimatedSquare Program
/** * This program tests the AnimatedSquare class by putting two squares * on the screen and having them move independently. */ public class TestAnimatedSquare extends GraphicsProgram { public void run() { double x1 = getWidth() / 3 - SIZE / 2; double x2 = 2 * getWidth() / 3 - SIZE / 2; double y = (getHeight() - SIZE) / 2; AnimatedSquare redSquare = new AnimatedSquare(SIZE); redSquare.setFilled(true); redSquare.setColor(Color.RED); add(redSquare, x1, y); AnimatedSquare greenSquare = new AnimatedSquare(SIZE); greenSquare.setFilled(true); greenSquare.setColor(Color.GREEN); add(greenSquare, x2, y); Thread redSquareThread = new Thread(redSquare); Thread greenSquareThread = new Thread(greenSquare); waitForClick(); redSquareThread.start(); greenSquareThread.start(); } /* Private constants */ private static final double SIZE = 75;
11
The AnimatedSquare Class
/** * This class creates an animated square that has its own thread of control. * Once started, the square moves in a random direction every time step. * After CHANGE_TIME time steps, the square picks a new random direction. */ public class AnimatedSquare extends GRect implements Runnable { /* Creates a new AnimatedSquare of the specified size */ public AnimatedSquare(double size) { super(size, size); } /* Runs when this object is started to animate the square */ public void run() { for (int t = 0; true; t++) { if (t % CHANGE_TIME == 0) { direction = rgen.nextDouble(0, 360); movePolar(DELTA, direction); pause(PAUSE_TIME); /* Private constants */ private static final double DELTA = 2; /* Pixels to move each cycle */ private static final int PAUSE_TIME = 20; /* Length of time step */ private static final int CHANGE_TIME = 50; /* Steps before changing direction */ /* Private instance variables */ private RandomGenerator rgen = RandomGenerator.getInstance(); private double direction;
12
public class TestAnimatedSquare extends GraphicsProgram {
public void run() { double x1 = getWidth() / 3 - SIZE / 2; double x2 = 2 * getWidth() / 3 - SIZE / 2; double y = (getHeight() - SIZE) / 2; AnimatedSquare redSquare = new AnimatedSquare(SIZE); redSquare.setFilled(true); redSquare.setColor(Color.RED); add(redSquare, x1, y); AnimatedSquare greenSquare = new AnimatedSquare(SIZE); greenSquare.setFilled(true); greenSquare.setColor(Color.GREEN); add(greenSquare, x2, y); Thread redSquareThread = new Thread(redSquare); Thread greenSquareThread = new Thread(greenSquare); waitForClick(); redSquareThread.start(); greenSquareThread.start(); } private static final double SIZE = 75;
13
public class AnimatedSquare extends GRect
implements Runnable { public AnimatedSquare(double size) { super(size, size); } public void run() { for (int t = 0; true; t++) { if (t % CHANGE_TIME == 0) { direction = rgen.nextDouble(0, 360); movePolar(DELTA, direction); pause(PAUSE_TIME); private static final double DELTA = 2; private static final int PAUSE_TIME = 20; private static final int CHANGE_TIME = 50; private RandomGenerator rgen = RandomGenerator.getInstance(); private double direction;
14
Race Conditions – Graphically
import acm.graphics.*; import acm.program.*; import java.awt.*; import java.awt.event.*; /** * This program illustrates race conditions in a graphical way. The shared * variable is in this case the canvas. Whenever the canvas is resized, two * threads paint the canvas, one all red, the other all black. If repaint() is * made synchronized, the color will be the same for the whole canvas. However, * we still cannot predict whether red or black wins. * rvh */ public class RectRace extends GraphicsProgram implements ComponentListener { // Enable listening to component resizing public void init() { addComponentListener(this); } // Whenever we get resized, start the two painting threads public void componentResized(ComponentEvent e) { RectPaint paintRed = new RectPaint(Color.RED); RectPaint paintBlack = new RectPaint(Color.BLACK); Thread threadRed = new Thread(paintRed); Thread threadBlack = new Thread(paintBlack); threadRed.start(); threadBlack.start(); public void componentHidden(ComponentEvent e) { public void componentMoved(ComponentEvent e) { public void componentShown(ComponentEvent e) { // Repaint the canvas in given color // Make this "synchronized" to ensure a consistently colored canvas private void repaint(Color color) { int width = getWidth(); int height = getHeight(); for (int y = 0; y < height; y += PIX) { for (int x = 0; x < width; x += PIX) { GRect pixel = new GRect(PIX, PIX); pixel.setColor(color); pixel.setFilled(true); add(pixel, x, y); private static final int PIX = 5; // Size of pixel /** The repainting thread class class RectPaint implements Runnable { // Constructor RectPaint(Color color) { this.color = color; // Call repaint method of RectRace public void run() { repaint(color); // The color in which this thread repaints the canvas private Color color;
15
public class RectRace extends GraphicsProgram
implements ComponentListener { // Enable listening to component resizing public void init() { addComponentListener(this); } // Whenever we get resized, start the painting threads public void componentResized(ComponentEvent e) { RectPaint paintRed = new RectPaint(Color.RED); RectPaint paintBlack = new RectPaint(Color.BLACK); Thread threadRed = new Thread(paintRed); Thread threadBlack = new Thread(paintBlack); threadRed.start(); threadBlack.start();
16
// Repaint the canvas in given color
// Make this "synchronized" for consistent canvas private void repaint(Color color) { int width = getWidth(); int height = getHeight(); for (int y = 0; y < height; y += PIX) { for (int x = 0; x < width; x += PIX) { GRect pixel = new GRect(PIX, PIX); pixel.setColor(color); pixel.setFilled(true); add(pixel, x, y); } private static final int PIX = 5; // Size of pixel
17
// The repainting thread class
class RectPaint implements Runnable { RectPaint(Color color) { this.color = color; } public void run() { repaint(color); private Color color;
18
Race Conditions – With Data
import acm.program.ConsoleProgram; import acm.util.ErrorException; /** * This program illustrates race conditions resulting from concurrent usage of * shared variables. * rvh */ public class DataRace extends ConsoleProgram { * Run method: Start two threads that access the integer "shared". The * "Incrementer" thread increments ITER_CNT times, the "Decrementer" * decrements. The resulting value of shared cannot be predicted, unless one * makes the method addDelta() synchronized. Making addDelta() synchronized * resolves the race conditions resulting from uncontrolled access of the * thread-unsafe function addDelta() and thus ensures that shared will * always end up being zero. However, this still does not resolve the * write-write race on delta. We also measure run times to see what effect * synchronized has. public void run() { setFont("Times New Roman-40"); while (true) { readLine("Hit enter for another run: "); // Start time measurement long startTime = System.nanoTime(); shared = 0; runThreads(); // Stop time measurement long estimatedTime = System.nanoTime() - startTime; // Print resulting shared variables + time usage println("shared = " + shared + ", delta = " + delta + ", after " + estimatedTime / 1000 / 1e3 + " msecs"); } // Run both threads private void runThreads() { // Create runnable objects Adder incr = new Adder("Incrementer", 1); Adder decr = new Adder("Decrementer", -1); // Create threads Thread incrThread = new Thread(incr); Thread decrThread = new Thread(decr); // Start threads decrThread.start(); incrThread.start(); try { // Wait for threads to complete incrThread.join(); decrThread.join(); } catch (InterruptedException e) { throw new ErrorException(e); // Add a (positive or negative) delta to shared // Making this synchronized avoids inconsistent state of "shared" // but does not avoid race on delta private void addDelta(int d) { delta = d; shared += delta; // Ivars of DataRace private int shared; private int delta; * The class from which we instantiate threads that work on shared class Adder implements Runnable { // Constructor public Adder(String name, int threadDelta) { this.name = name; this.threadDelta = threadDelta; // Do the work of the thread for (int i = 0; i < ITER_CNT; i++) { addDelta(threadDelta); println(name + " has finished."); // Ivars of Adder private String name; private int threadDelta; // How many iterations we do private static final int ITER_CNT = ;
19
public class DataRace extends ConsoleProgram {
public void run() { while (true) { readLine("Hit enter for another run: "); long start = System.nanoTime(); shared = 0; runThreads(); long time = System.nanoTime() - start; println("shared = " + shared + ", delta = " + delta + ", after " + time / 1000 / 1e3 + " msecs"); }
20
private void runThreads() {
Adder incr = new Adder("Incrementer", 1); Adder decr = new Adder("Decrementer", -1); Thread incrThread = new Thread(incr); Thread decrThread = new Thread(decr); decrThread.start(); incrThread.start(); try { incrThread.join(); decrThread.join(); } catch (InterruptedException e) { throw new ErrorException(e); }
21
addDelta() is not thread-safe
private void addDelta(int d) { delta = d; shared += delta; } // Ivars of DataRace private int shared; private int delta; addDelta() is not thread-safe To avoid interruption, make it synchronized However, this still does not resolve write-write race on delta!
22
class Adder implements Runnable {
public Adder(String name, int threadDelta) { this.name = name; this.threadDelta = threadDelta; } public void run() { for (int i = 0; i < ITER_CNT; i++) { addDelta(threadDelta); println(name + " has finished."); private String name; private int threadDelta; static private final int ITER_CNT = ;
23
Periodic Tasks public BallModel() { ... timer = new Timer(BALL_CYCLE);
} public void run() { while (true) { move(); timer.pause(); private Timer timer; private static final int BALL_CYCLE = 25;
24
public Timer(double period) { this.period = period; reset(); }
public class Timer { public Timer(double period) { this.period = period; reset(); } public void reset() { nanoTime = System.nanoTime(); delay = period; package breakout; import acm.util.JTFTools; /** * Class that supports executing tasks at a certain period. This can be used as * follows: * - Construct a Timer object with desired period * - Call reset() whenever periodic execution should (re-)start * - Call pause() once at each period, e.g. * while (true) { * // Do something, like moving an object * timer.pause(); * } * rvh */ public class Timer { * Constructor period * The desired cycle period, in msec public Timer(double period) { this.period = period; reset(); } * Resets the timer. public void reset() { nanoTime = System.nanoTime(); delay = period; * Method to call at each cycle. A delay adjusts itself such that the cycle * period approaches the desired value. public void pause() { double preNanoTime = nanoTime; // cycleTime indicates (in msec) the time passed between the previous // call of pause() and this time of pause(). // Ideally, it would be cycleTime == period. double cycleTime = (nanoTime - preNanoTime) / 1e6; // rawDelayAdjustment indicates the difference between the measured // cycleTime and the desired period. // Ideally, it would be rawDelayAdjustment == 0. // rawDelayAdjustment < 0 indicates cycleTime > period, i.e., the last // cycle took too long. // rawDelayAdjustment > 0 indicates cycleTime < period, i.e., the last // cycle was too short. double rawDelayAdjustment = period - cycleTime; // delayAdjustment indicates how much the current delay (see below) // should change such that the actual cycleTime becomes the desired // period. // Conceptually, the delayAdjustment should be the rawDelayAdjustment // computed above. However, to avoid an oscillation in this control // loop, we do not use the full rawDelayAdjustment, but apply some // dampening factor DAMPENING, which should be between 0 and 1. double delayAdjustment = DAMPENING * rawDelayAdjustment; // delay indicates how long we have to pause such that the cycle time is // (approximately) the desired period. delay += delayAdjustment; // If delay is positive, we have to pause. // If delay stays negative for many cycles, with a high amount, this // means that the measured cycle time is significantly longer than the // desired period even without any extra delay. I.e., we degrade to a // "run as fast as possible mode". Then the whole Timer business has no // effect, and we should probably think about increasing our period or // decreasing the computational load of each cycle. // A possible extension would be to watch for this case and to react // somehow to it, e.g. by printing a warning on the console. if (delay > 0) { JTFTools.pause(delay); // Ivars private double nanoTime; private double period; private double delay; // Constant // Dampening factor used in pause method, should be between 0 and 1 private static final double DAMPENING = 0.1;
25
public void pause() { double preNanoTime = nanoTime; nanoTime = System.nanoTime(); double cycleTime = (nanoTime - preNanoTime) / 1e6; double rawDelayAdjustment = period - cycleTime; double delayAdjustment = DAMPENING * rawDelayAdjustment; delay += delayAdjustment; if (delay > 0) { JTFTools.pause(delay); }
26
private double nanoTime;
private double period; private double delay; private static final double DAMPENING = 0.1; }
27
Summary I Good software engineering makes use of patterns, as proposed by Gang of Four One important pattern is Model-View-Controller (MVC) Concurrency is an powerful yet tricky programming concept The Java event model already entails concurrency Java also supports concurrency with user-level threads A thread can be constructed with a runnable object, which is constructed from a class that implements the Runnable interface Randomness in games
28
Summary II The runnable object needs a run method
After creating a thread with a Thread constructor, we must still explicitly start it We can wait for completion of a thread with join The concurrent use of shared may result in write-write races or write-read races Some, but not all, race conditions can be avoided by acquiring a monitor lock (or simply lock) on an object One way to get a lock on an object is to call a synchronized method of it Randomness in games
29
Programming Problem Pouncer Prize
This is to certify that ... has achieved ... out of 1100 Points in the problem sets of the Imperative and Object-Oriented Programming (InfProgOO) class conducted in the Winter Semester 2016/17 at the Department of Computer Science, Kiel University, the third-highest score among 310 class participants. Kiel, January 31 st, 2017 ___________________ Prof. Reinhard von Hanxleden 3 rd
30
We Want You for future InfProgOO ! Apply now to rvh@...
[Wikimedia Commons]
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.