CS396N - Web Programming |
Spring 2002 |
Chapter 16 - Concurrent Programming with Threads |
Loading images - Redux
The following program loads an image from disk within a rectangular area:
public class Image2 extends Applet{
picture = getImage(codebase,"ny1.jpg");
Note
Using off-screen buffering
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
// An applet that draws a small circle where you click.
public class DrawCircles extends Applet {
private Vector circles;
/** When you click the mouse, create a SimpleCircle,
* put it in the Vector, and tell the system
* to repaint (which calls update, which clears
* the screen and calls paint).
*/
private class CircleDrawer extends MouseAdapter {
public void mousePressed(MouseEvent event)
{
circles.addElement(
new SimpleCircle(event.getX(),event.getY(),25));
repaint();
}
}
public void init() {
circles = new Vector();
addMouseListener(new CircleDrawer());
setBackground(Color.white);
}
/** This loops down the available SimpleCircle objects,
* drawing each one.
*/
public void paint(Graphics g) {
SimpleCircle circle;
for(int i=0; i<circles.size(); i++) {
circle = (SimpleCircle)circles.elementAt(i);
circle.draw(g);
}
}
}
import java.awt.*;
/** A class to store an x, y, and radius, plus a draw method.
*/
public class SimpleCircle {
private int x, y, radius;
public SimpleCircle(int x, int y, int radius) {
setX(x);
setY(y);
setRadius(radius);
}
/** Given a Graphics, draw the SimpleCircle
* centered around its current position.
*/
public void draw(Graphics g) {
g.fillOval(x - radius, y - radius,
radius * 2, radius * 2);
}
public int getX() { return(x); }
public void setX(int x) { this.x = x; }
public int getY() { return(y); }
public void setY(int y) { this.y = y; }
public int getRadius() { return(radius); }
public void setRadius(int radius) {
this.radius = radius;
}
}
Example 2 - Bounce - Uses Paint to Draw - Shows Flicker
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
/** Bounce circles around on the screen. Doesn't use double
* buffering, so has problems with overlapping circles.
* Overrides update to avoid flicker problems.
*/
public class Bounce extends Applet implements Runnable,
ActionListener {
private Vector circles;
private int width, height;
private Button startButton, stopButton;
private Thread animationThread = null;
public void init() {
setBackground(Color.white);
width = getSize().width;
height = getSize().height;
circles = new Vector();
startButton = new Button("Start a circle");
startButton.addActionListener(this);
add(startButton);
stopButton = new Button("Stop all circles");
stopButton.addActionListener(this);
add(stopButton);
}
/** When the "start" button is pressed, start the animation
* thread if it is not already started. Either
way, add a
* circle to the Vector of circles that are
being bounced.
* <P>
* When the "stop" button is pressed, stop
the thread and
* clear the Vector of circles.
*/
public void actionPerformed(ActionEvent event) {
if (event.getSource() == startButton) {
if (circles.size() == 0) {
// Erase any circles
from previous run.
getGraphics().clearRect(0,
0, getSize().width,
getSize().height);
animationThread =
new Thread(this);
animationThread.start();
}
int radius = 25;
int x = radius + randomInt(width
- 2 * radius);
int y = radius + randomInt(height
- 2 * radius);
int deltaX = 1 + randomInt(10);
int deltaY = 1 + randomInt(10);
circles.addElement(new MovingCircle(x,
y, radius, deltaX,
deltaY));
} else if (event.getSource() == stopButton)
{
if (animationThread != null)
{
animationThread =
null;
circles.removeAllElements();
}
}
repaint();
}
/** Each time around the loop, call paint and then take
a
* short pause. The paint method will move
the circles and
* draw them.
*/
public void run() {
Thread myThread = Thread.currentThread();
// Really while animationThread not null
while(animationThread==myThread) {
repaint();
pause(100);
}
}
/** Skip the usual screen-clearing step of update so that
* there is no flicker between each drawing
step.
*/
public void update(Graphics g) {
paint(g);
}
/** Erase each circle's old position, move it, then draw
it
* in new location.
*/
public void paint(Graphics g) {
MovingCircle circle;
for(int i=0; i<circles.size(); i++) {
circle = (MovingCircle)circles.elementAt(i);
g.setColor(getBackground());
circle.draw(g); // Old
position.
circle.move(width, height);
g.setColor(getForeground());
circle.draw(g); // New
position.
}
}
// Returns an int from 0 to max (inclusive),
// yielding max + 1 possible values.
private int randomInt(int max) {
double x =
Math.floor((double)(max + 1)
* Math.random());
return((int)(Math.round(x)));
}
// Sleep for the specified amount of time.
private void pause(int milliseconds) {
try {
Thread.sleep((long)milliseconds);
} catch(InterruptedException ie) {}
}
}
Example 3 - Double Buffered Bounce
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
/** Bounce circles around on the screen, using double buffering
* for speed and to avoid problems with overlapping
circles.
* Overrides update to avoid flicker problems.
*/
public class DoubleBufferBounce extends Applet implements Runnable,
ActionListener {
private Vector circles;
private int width, height;
private Image offScreenImage;
private Graphics offScreenGraphics;
private Button startButton, stopButton;
private Thread animationThread = null;
public void init() {
setBackground(Color.white);
width = getSize().width;
height = getSize().height;
offScreenImage = createImage(width, height);
offScreenGraphics = offScreenImage.getGraphics();
// Automatic in some systems, not in others.
offScreenGraphics.setColor(Color.black);
circles = new Vector();
startButton = new Button("Start a circle");
startButton.addActionListener(this);
add(startButton);
stopButton = new Button("Stop all circles");
stopButton.addActionListener(this);
add(stopButton);
}
/** When the "start" button is pressed, start the animation
* thread if it is not already started. Either
way, add a
* circle to the Vector of circles that are
being bounced.
* <P>
* When the "stop" button is pressed, stop
the thread and
* clear the Vector of circles.
*/
public void actionPerformed(ActionEvent event) {
if (event.getSource() == startButton) {
if (circles.size() == 0) {
animationThread =
new Thread(this);
animationThread.start();
}
int radius = 25;
int x = radius + randomInt(width
- 2 * radius);
int y = radius + randomInt(height
- 2 * radius);
int deltaX = 1 + randomInt(10);
int deltaY = 1 + randomInt(10);
circles.addElement(new MovingCircle(x,
y, radius, deltaX,
deltaY));
repaint();
} else if (event.getSource() == stopButton)
{
if (animationThread != null)
{
animationThread =
null;
circles.removeAllElements();
}
}
}
/** Each time around the loop, move each circle based
on its
* current position and deltaX/deltaY values.
These values
* reverse when the circles reach the edge
of the window.
*/
public void run() {
MovingCircle circle;
Thread myThread = Thread.currentThread();
// Really while animationThread not null.
while(animationThread==myThread) {
for(int j=0; j<circles.size();
j++) {
circle = (MovingCircle)circles.elementAt(j);
circle.move(width,
height);
}
repaint();
pause(100);
}
}
/** Skip the usual screen-clearing step of update so that
* there is no flicker between each drawing
step.
*/
public void update(Graphics g) {
paint(g);
}
/** Clear the off-screen pixmap, draw each circle onto
it, then
* draw that pixmap onto the applet window.
*/
public void paint(Graphics g) {
offScreenGraphics.clearRect(0, 0, width,
height);
MovingCircle circle;
for(int i=0; i<circles.size(); i++) {
circle = (MovingCircle)circles.elementAt(i);
circle.draw(offScreenGraphics);
}
g.drawImage(offScreenImage, 0, 0, this);
}
// Returns an int from 0 to max (inclusive), yielding
max + 1
// possible values.
private int randomInt(int max) {
double x = Math.floor((double)(max + 1) *
Math.random());
return((int)(Math.round(x)));
}
// Sleep for the specified amount of time.
private void pause(int milliseconds) {
try {
Thread.sleep((long)milliseconds);
} catch(InterruptedException ie) {}
}
}
Animation
The following program first loads ten gif images into an Image array. Then displays one after the other in an infinite loop, with a time delay.
import java.awt.*;
import java.applet.*;
import java.net.*;
public class Animation1 extends
Applet {
public void init() {
for (int i=1; i < 13; i++)
{
mickeys[i]=getImage(codebase,"animation/mickey"+i+".gif");
next = 1;
public void paint(Graphics g) {
for (int i =0; i<=20000000;i++){}
next++;
if (next > 10) next = 1;
repaint();
}
What are threads?
implements interface | The main class in your applet is
public class classname extends Applet implements Runnable { } |
Define run() method | public void run(){
} This method includes statements to be executed by the thread. |
Create a thread | Create an object of Thread class.
Thread thread1 = new Thread(this); You normally do this in the init() method. |
Start thread | Start the thread that you have already
created.
thread1.start(); This will start the thread by executing the run() method. |
Example
The following program is same as animation1.java, except that this uses threads.
import java.awt.*;
import java.applet.*;
import java.net.*;
public class Animation2 extends
Applet implements Runnable {
Thread thread1;
int next;
public void init() {
for (inti=1; I<11; I++) {
public void run() {
public void paint(Graphics g) {
import java.awt.*;
import java.util.Calendar;
import java.applet.*;
public class DigitalClock extends Applet implements Runnable{
Thread clockThread1 = null, clockThread2 = null;
Font font = new Font("Courier", Font.BOLD, 48);
Color color = Color.green;
public void start(){
if (clockThread1 == null){
clockThread1 = new Thread(this);
clockThread1.start();
}
if (clockThread2 == null){
clockThread2 = new Thread(this);
clockThread2.start();
}
}
public void stop(){
clockThread1 = null;
clockThread2 = null;
}
public void run(){
while(Thread.currentThread() == clockThread1){
color = Color.green;
repaint();
try{
Thread.currentThread().sleep(1000);
} catch (InterruptedException e){}
}
while(Thread.currentThread() == clockThread2){
color = Color.blue;
repaint();
try{
Thread.currentThread().sleep(1500);
} catch (InterruptedException e){}
}
}
public void paint(Graphics g){
Calendar calendar = Calendar.getInstance();
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute =calendar.get(Calendar.MINUTE);
int second =calendar.get(Calendar.SECOND);
g.setFont(font);
g.setColor(color);
g.drawString(hour + ":"+minute/10+minute%10+
":"+second/10+second%10, 10,60);
}
}
Example - Stock Ticker
import java.awt.*;
import java.util.*;
import java.applet.*;
public class Ticker extends Applet implements Runnable{
Thread clockThread1 = null, clockThread2 = null;
Font font = new Font("Courier", Font.BOLD, 36);
Color color = Color.green;
String [] names = {"BBRC","RMBS","HON ","BOA ", "TNB ",
"AT&T"};
int [] prices = { 94,
115, 35, 43,
20, 34};
Dimension d;
int x,y,decrement=2;
Random num;
boolean updated_prices = false;
public void init(){
d = getSize();
x = d.width;
y = font.getSize();
num = new Random();
}
public void start(){
if (clockThread1 == null){
clockThread1 = new Thread(this);
clockThread1.start();
}
if (clockThread2 == null){
clockThread2 = new Thread(this);
clockThread2.start();
}
}
public void stop(){
clockThread1 = null;
clockThread2 = null;
}
public void run(){
// This thread calls the paint() method to display stocks
while(Thread.currentThread() == clockThread1){
repaint();
try{
Thread.currentThread().sleep(100);
} catch (InterruptedException e){}
}
//This thread updates the stock prices with a random
// increment or decrement
while(Thread.currentThread() == clockThread2){
int change;
int up_down;
for(int i=0;i<prices.length;i++){
change = num.nextInt()%3;
up_down = num.nextInt()%2;
if(up_down==0)
change = -change;
prices[i] += change;
}
updated_prices = true;
try{
Thread.currentThread().sleep(1000);
} catch (InterruptedException e){}
}
}
public void paint(Graphics g){
//The following strings contain all the stock symbols
// and stock totals for display by paint() method
String symbols = new String();
String tots = new String();
g.setFont(font);
FontMetrics fm =g.getFontMetrics();
for(int i =0;i<names.length;i++)
symbols += names[i]+" ";
for(int i =0;i<prices.length;i++){
tots += " "+ prices[i];
if (prices[i]<100 && prices[i]>0)
tots += " ";
else
tots += " ";
}
// calculte total length of string
int length = fm.stringWidth(symbols);
//draw region with symbols
g.setColor(Color.black);
g.fillRect(0,0,d.width,y);
g.setColor(Color.white);
g.drawString(symbols,x,y);
// draw region with prices
g.setColor(Color.black);
g.fillRect(0,y,d.width,y);
if(updated_prices)
g.setColor(Color.blue);
else
g.setColor(Color.green);
g.drawString(tots,x,y+y-10);
updated_prices = false;
//move text one unit to left for next frame
x -= decrement;
// if text is completely off to left
// move position back to right
if(x < -length) x = d.width;
}
}
Including sound files
The following program illustrates the basic way of playing sound files.
public class Sound1 extends Applet{
|
|
soundclip.play() | Plays the sound clip |
soundclip.stop() | Stops the sound clip |
sound.loop() | Plays the sound clip repeatedly. |
Example
The following program displays two buttons - play, and stop. It plays music, and you can control playing or stopping music using buttons.
public class Sound2 extends Applet implements ActionListener{
public void init(){
public void actionPerformed(ActionEvent event){