• No results found

To be a Thread, your class should be a subclass of the Thread class. It contains some important methods: Threads

N/A
N/A
Protected

Academic year: 2022

Share "To be a Thread, your class should be a subclass of the Thread class. It contains some important methods: Threads"

Copied!
28
0
0

Loading.... (view fulltext now)

Full text

(1)

Threads 30 January 2003 1

Threads

Threads are used to achieve parallelism within your program. This is handled completely inside the JVM and there is no extra processes created. If you are working on a single processor compu- ter, there will only be simulated paral- lelism.

Threads 30 January 2003 2

To be a Thread, your class should be a subclass of the Thread class. It contains some important methods:

start, start the thread

stop, stop the thread, deprecated.

sleep, delay for a while run, the thread

suspend, pause, deprecated destroy, cleanup

resume, restart after a suspend, deprecated

interrupt, ask the thread to stop interrupted, check if current thread is interrupted

isInterrupted, check if interrupted isAlive, check if alive

Threads 30 January 2003 3

The procedure to create a thread is

declare a class as a subclass to Thread.

implement the run method, this is the thread we are to create

instansiate your class

call the start method of your class.

Threads 30 January 2003 4

This procedure is sometimes impossi- ble to implement.

Why?

(2)

Threads 30 January 2003 5

If you want an applet to contain a thread, it should extend Thread. How- ever an applet already extends Applet and can extend no more classes.

To solve this there is an interface, Run- nable, that can be implemented.

Threads 30 January 2003 6

The procedure to use this interface is

Let your class implement the Runna- ble interface. This means that you should implement the run method

declare a Thread as an instance varia- ble in your class

instantiate the Thread, give this (a Runnable) as parameter to the con- structor.

start the Thread, using the start method.

Threads 30 January 2003 7

A simple example looks like this

public class Trad extends Thread{

String s;

int d;

public Trad() { s = "";

d = 1000;

}

public Trad(String a, int delay) { s = a;

d = delay;

}

public void run() { while (true) {

System.out.println(s);

try {

sleep(d); // a Thread method }

catch (InterruptedException e) {} // do nussing

} }

public static void main(String [] s) {

Trad t1 = new Trad("Ettan", 100);

Trad t2 = new Trad("Tvåan", 175);

Threads 30 January 2003 8

t1.start(); t2.start();

try {

sleep(10000);

}

catch (InterruptedException e) {}

t1.stop();

t2.stop();

} }

This creates two instances of the Trad class and starts their threads. This will cause two parallel executions of the run method in two different objects. It will go on for a while until stopped by the main thread.

NOTE: stop is deprecated in Java2 and

should not be used. This is because it

cannot be guaranteed to leave the locks

in a proper state. This is just for demo

purpose.

(3)

Threads 30 January 2003 9

The output of this will be

> java Trad Ettan Tvåan Ettan Tvåan Ettan Ettan Tvåan Ettan Ettan Tvåan Ettan Ettan Tvåan Ettan Tvåan Ettan Ettan .... etc

Threads 30 January 2003 10

If I’m not supposed to use stop, how do I stop a thread?

public class Trad2 extends Thread{

String s;

int d;

public Trad2() { s = "";

d = 1000;

}

public Trad2(String a, int delay) { s = a;

d = delay;

}

public void run() {

while (!interrupted()) { System.out.println(s);

try { sleep(d);

}

catch (InterruptedException e) {break;}

} }

public static void main(String [] s) { Trad2 t1 = new Trad2("Ettan", 100);

Trad2 t2 = new Trad2("Tvåan", 175);

t1.start(); t2.start();

try {

sleep(10000);

}

catch (InterruptedException e) {}

t1.interrupt();

t2.interrupt();

} }

Threads 30 January 2003 11

Here I politely ask the thread to termi- nate. It might ignore me, but here we test the flag and terminate in a control- led way when asked to.

Why do we have a break in the catch?

Because if we are interrupted while sleeping, we will end up in the catch clause.

Threads 30 January 2003 12

An applet has some additional meth- ods

init, the setup method

stop, called when we leave this page start, called when we are back

destroy, called when the browser exits

Please note the difference between

start/stop in the Thread class and

start/stop in the Applet class.

(4)

Threads 30 January 2003 13

A digital clock implemented as an applet.

import java.applet.*;

import java.awt.*;

import java.util.*;

import java.awt.event.*;

//

// A digital clock applet //

public class Digital extends Applet implements Runnable {

private TextField h,m,s;

private Calendar cal; // current time private int hour,minute,second;

// extracted data private Label a,b,c; // some labels private Thread t; // our thread

public void init() { // get current time

cal = Calendar.getInstance();

setLayout(new GridLayout(2,3));

// extract h, m, s

hour = cal.get(Calendar.HOUR_OF_DAY);

minute = cal.get(Calendar.MINUTE);

second = cal.get(Calendar.SECOND);

a = new Label("Hour");

// add labels b = new Label("Min");

c = new Label("Sec");

h = new TextField(String.valueOf(hour));

m = new TextField(String.valueOf(minute));

s = new TextField(String.valueOf(second));

add(a); add(b); add(c);

add(h); add(m); add(s);

Threads 30 January 2003 14

// Disable editing of the textfields h.setEditable(false);

m.setEditable(false);

s.setEditable(false);

// create and start a thread t = new Thread(this);

t.start();

}

public void stop() { if (t != null) { t.interrupt();

t = null;

} }

public void start() { if (t == null) { t = new Thread(this);

t.start();

} }

public void destroy() { t.destroy();

t = null;

}

public void run() {

while (!Thread.interrupted()) { try {

Thread.sleep(200);

}

catch (InterruptedException e) {break;}

// get the time cal = Calendar.getInstance();

// extract data

hour = cal.get(Calendar.HOUR_OF_DAY);

minute = cal.get(Calendar.MINUTE);

Threads 30 January 2003 15

second = cal.get(Calendar.SECOND);

// update the clock

h.setText(String.valueOf(hour));

m.setText(String.valueOf(minute));

s.setText(String.valueOf(second));

} } };

This creates and starts a thread. When the browser selects another page, the thread is stopped. When the browser returns to this page, a new thread is created and started.

Note that the methods sleep and inter- rupt are prefixed with Thread. Why?

Threads 30 January 2003 16

Parallelism causes problems with data access. If you have several actors modi- fying and reading the same data, there is no way to tell the order of these accesses. If you have an array or other structure, it might be that part of the array have been written when you are to read it.

A class that handles these problems

properly is threadsafe.

(5)

Threads 30 January 2003 17

Assume that there are two persons that use a bank account. Both try to add 1000 SEK to the account at the same time. Assume that the balance is 1500 SEK. It might look like

Person 1 Person 2 --- --- Get the balance

Get the balance Add 1000

Set new balance as 2500 SEK

Add 1000 Set new balance as 2500 SEK.

1000 SEK got lost somewhere!

Threads 30 January 2003 18

To handle this we have to make sure that only one actor at a time have access to critical data. To do that we declare a method as synchronized class Account {

private double balance;

public synchronized void

transaction(double amount) { if (amount + balance <0) return;

else balance = balance+ amount;

} ....

}

Threads 30 January 2003 19

This adds a lock to the object so that there can only be one actor executing this method at a time. The lock is appli- cable to all synchronized methods in an object.

Thus, only one synchronized method in an object can be executed at any time.

Threads 30 January 2003 20

The locking mechanism is object based, which means that all other syncronized methods in the object are locked out, but not static methods. Also it does not affect other objects of the same class.

A static method may be declared syn-

chronized, and will then be synchro-

nized over all instances of the class.

(6)

Threads 30 January 2003 21

To properly synchronize data that is accessed by several objects, all accesses should be routed through a common object that can be synchronized.

Let us study a consumer-producer situ- ation where we have different threads producing and consuming data. This adds an additional problem, there might not be any data produced when a consumer requests it.

Threads 30 January 2003 22

To handle this, two additional tools are available, wait and notify. These are declared in the Object class.

If a consumer finds that there is no data available, it puts itself into wait mode by calling wait. This also releases the locks it might have acquired.

When a producer produces some data it calls notify to generate a signal that there is data available. This will release one of the waiting threads (if there are any).

Threads 30 January 2003 23

We have a simple queue where a pro- ducer stores numbers and a consumer gets numbers. Things are properly syn- chronized and accesses are done through an object of the queue type.

We are using wait and notify to handle the “empty/full” queue problem.

Since dequeue and enqueue are both in the same object, since both are declared synchronized and since the data array is local to the object, this class is thread- safe.

Threads 30 January 2003 24

// first a simple main that starts up everything public class QTest {

public static void main(String args[]) { Q q = new Q();

new Producer(q);

new Consumer(q);

} }

// the queue class public class Q {

private final int BUF_SIZE = 5;

private int buf[] = new int[BUF_SIZE];

private int start = 0;

private int current_size = 0;

// dequeueing

public synchronized int get() { if (current_size == 0) { try {

System.out.println("Queue empty in get");

wait();

}

catch (InterruptedException e) { }

}

// get the value, adjust the start pointer int return_value = buf[start++];

start %= BUF_SIZE;

// check if the queue was full, if so // signal that there is space left now.

if ((current_size--) == BUF_SIZE) { notify();

}

(7)

Threads 30 January 2003 25

return return_value;

}

// the enqueue method

public synchronized void put(int val) { if (current_size == BUF_SIZE) { try {

System.out.println("Queue full in put");

wait();

}

catch (InterruptedException e) { }

}

current_size++;

buf[(start+current_size) % BUF_SIZE] = val;

if (current_size==1) notify();

} }

// The producer class

public class Producer implements Runnable { private Q q; // the queue public Producer(Q q) {

this.q = q;

new Thread(this, "Producer").start();

}

//the “thread”

public void run() { int i = 0;

while(true) {

System.out.println("put " + i);

try {

if ((i/10)%2 == 0) Thread.sleep(100);

}

catch (InterruptedException e) { }

Threads 30 January 2003 26

q.put(i++);

} } } //

public class Consumer implements Runnable { private Q q;

public Consumer(Q q) { this.q = q;

new Thread(this, "Consumer").start();

}

public void run() { int i;

while(true) {

System.out.println("get");

i = q.get();

try {

if ((i/10)%2 == 1) Thread.sleep(100);

}

catch (InterruptedException e) { }

} } }

Threads 30 January 2003 27

If you run this you will get

get

Queue empty in get put 0

put 1 get

Queue empty in get put 2

get

Queue empty in get put 3

get

Queue empty in get put 4

get

Queue empty in get put 5

get

Queue empty in get put 6

get

Queue empty in get put 7

get

Queue empty in get put 8

get

Queue empty in get put 9

get

Queue empty in get put 10 put 11 put 12 put 13

Threads 30 January 2003 28

put 14 Queue full in put put 15 Queue full in put get

put 16 Queue full in put get

put 17 Queue full in put get

put 18 Queue full in put get

put 19 Queue full in put get

put 20 Queue full in put get

put 21 Queue full in put get

get put 22 ....

(8)

Threads 30 January 2003 29

Recent versions of Java contain tools to simplify threading.

Instead of an explicit thread you can use a scheduler, that schedules your parallel stuff at prescribed times. It might look like:

Threads 30 January 2003 30

import java.util.*;

public class Trad3 extends TimerTask{ // A timertask!!

String s;

public Trad3() { s = "";

}

public Trad3(String a) { s = a;

}

public void run() { System.out.println(s);

}

public static void main(String [] s) { Timer t = new Timer(); // our timer // create our two “threads”

Trad3 t1 = new Trad3("Ettan");

Trad3 t2 = new Trad3("Tvåan");

// schedule start and interval t.schedule(t1,100,100);

t.schedule(t2,175,175);

// wait 10s try {

Thread.sleep(10000);

}

catch (InterruptedException e) {}

// kill the timer and the “threads”

t1.cancel();

t2.cancel();

t.cancel();

} }

Threads 30 January 2003 31

When working with Swing and threads some special precautions must be taken.

Any action that changes any graphical component must be performed “in the eventhandling thread” to get proper synchronization.

The only exception to this is the con- structor (or init method). You may add and change components in the con- structor but not after the call to setVisi- ble.

Threads 30 January 2003 32

What does it mean?

You should do things that affect your graph in an event handler. An event- handler is always executing in the

“eventhandling thread”.

(9)

Threads 30 January 2003 33

There are different techniques that can be used here.

You can use some kind of timers or use explicit threads.

Threads 30 January 2003 34

To see an example we can use the Timer class (not to be confused with the Timer class). This Timer class is in javax.swing and the previous one is in java.util!

The Timer class is a simple clock that we can set to go off now and then.

When it do so, it generates an event that we can use.

An EXAMPLE!

Threads 30 January 2003 35

import javax.swing.*;

import java.awt.*;

import java.util.*;

import java.awt.event.*;

//

// A digital clock applet, testing javax.swing.Timer //

public class JDigital extends JApplet implements ActionListener {

private JTextField h,m,s; // 3 fields in the clock private Calendar cal; // current time private int hour,minute,second; // extracted data private JLabel a,b,c; // some labels // create a timer that goes off each 200 ms //this acts as a listener

// qualification of the name is needed because // we are importing util too, giving us both Timer // tasks visible.

private javax.swing.Timer t =

new javax.swing.Timer(200,this);

public void init() {

Container cont = getContentPane();

cal = Calendar.getInstance(); // get current time cont.setLayout(new GridLayout(2,3)); // gridlayout hour = cal.get(Calendar.HOUR_OF_DAY); // extract minute = cal.get(Calendar.MINUTE);

second = cal.get(Calendar.SECOND);

a = new JLabel("Hour"); // add labels b = new JLabel("Min");

c = new JLabel("Sec");

h = new JTextField(String.valueOf(hour)); // data m = new JTextField(String.valueOf(minute));

s = new JTextField(String.valueOf(second));

cont.add(a); cont.add(b); cont.add(c);

cont.add(h); cont.add(m); cont.add(s);

// Disable editing of the textfields h.setEditable(false); m.setEditable(false);

s.setEditable(false);

// start a Timer t.start();

}

Threads 30 January 2003 36

public void start() {

System.out.println("Start");

t.restart();

}

public void stop() {

System.out.println("Stop");

t.stop();

}

// Change things in here, in the “Event thread”

public void actionPerformed(ActionEvent e) { // get the time

cal = Calendar.getInstance();

// extract data

hour = cal.get(Calendar.HOUR_OF_DAY);

minute = cal.get(Calendar.MINUTE);

second = cal.get(Calendar.SECOND);

// update the clock

h.setText(String.valueOf(hour));

m.setText(String.valueOf(minute));

s.setText(String.valueOf(second));

} };

(10)

Threads 30 January 2003 37

You can also use explicit threads as we did in the awt example. Then we have some other tools available:

import javax.swing.*;

import java.awt.*;

import java.util.*;

import java.awt.event.*;

//

// A digital clock applet // testing some swing tools //

public class JDigital2 extends JApplet implements Runnable {

private JTextField h,m,s; // 3 fields in the clock private Calendar cal; // current time private int hour,minute,second; // extracted data private JLabel a,b,c; // some labels private Thread t; // our thread

public void init() {

Container cont = getContentPane();

cal = Calendar.getInstance(); // get current time cont.setLayout(new GridLayout(2,3)); //gridlayout hour = cal.get(Calendar.HOUR_OF_DAY); // h, m, s minute = cal.get(Calendar.MINUTE);

second = cal.get(Calendar.SECOND);

a = new JLabel("Hour"); // add labels b = new JLabel("Min");

c = new JLabel("Sec");

h = new JTextField(String.valueOf(hour)); // data m = new JTextField(String.valueOf(minute));

s = new JTextField(String.valueOf(second));

cont.add(a); cont.add(b); cont.add(c);

cont.add(h); cont.add(m); cont.add(s);

// Disable editing of the textfields h.setEditable(false); m.setEditable(false);

s.setEditable(false);

// create and start a thread t = new Thread(this);

t.start();

Threads 30 January 2003 38

}

public void stop() { if (t != null) { t.interrupt();

t = null;

} }

public void start() { if (t == null) { t = new Thread(this);

t.start();

} }

public void destroy() { if (t != null) t.destroy();

t = null;

}

// now, instead of updating the graph, spawn another // thread that updates the graph in the proper way.

public void run() {

Updater r = new Updater();

while (!Thread.interrupted()) { try {

Thread.sleep(200);

}

catch (InterruptedException e) {break;}

SwingUtilities.invokeLater(r); //!!!!!

} }

// internal help class

class Updater implements Runnable { public void run() {

// get the time

cal = Calendar.getInstance();

// extract data

hour = cal.get(Calendar.HOUR_OF_DAY);

minute = cal.get(Calendar.MINUTE);

second = cal.get(Calendar.SECOND);

Threads 30 January 2003 39

// update the clock

h.setText(String.valueOf(hour));

m.setText(String.valueOf(minute));

s.setText(String.valueOf(second));

} } };

Threads 30 January 2003 40

An example with moving objects is the following:

This draws a rotating and moving pol- ygon.

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

public class JPolyCanvas extends JPanel implements Runnable {

private Thread aktivitet;

int n, r, x0, y0;

double vinkel;

int[] x,y;

final double dv = 5*2*Math.PI/360;

double vrid = 0;

int xSteg = 4, ySteg = 3;

boolean first = true;

public JPolyCanvas() { this(7, 100);

}

public JPolyCanvas(int number, int radius) { n = number;

r = radius;

x = new int[n];

y = new int[n];

vinkel = 2*Math.PI/n;

}

public void start() { if (aktivitet == null ) { aktivitet = new Thread(this);

aktivitet.start();

} }

public void stop() { if (aktivitet != null ) { aktivitet.interrupt();

aktivitet = null;

(11)

Threads 30 January 2003 41

} }

public void destroy() { if (aktivitet != null ) { aktivitet.destroy();

} }

public void run() {

Updater r = new Updater();

while (!aktivitet.interrupted()) { try {

Thread.sleep(200);

}

catch (InterruptedException e) {break;}

SwingUtilities.invokeLater(r);

} }

class Updater implements Runnable { public void run () {

vrid = vrid + dv;

if (vrid > 2*Math.PI) vrid -= 2*Math.PI;

if (x0 - r + xSteg < 0 ||

x0 + r + xSteg > getSize().width) xSteg = -xSteg;

x0 += xSteg;

if (y0 - r + ySteg < 0 ||

y0 + r + ySteg > getSize().height) ySteg = -ySteg;

y0 += ySteg;

repaint();

} }

public void paintComponent(Graphics g) {

super.paintComponent(g);

if (first) {

x0 = getSize().width/2;

y0 = getSize().height/2;

first = false;

}

for (int i=0; i < n; i++) {

double v = i*vinkel - vrid;

x[i] = x0 + (int)Math.round(r * Math.cos(v));

Threads 30 January 2003 42

y[i] = y0 - (int)Math.round(r * Math.sin(v));

}

g.fillPolygon(x, y, n);

} }

Threads 30 January 2003 43

To use this we can do

import java.awt.*;

import javax.swing.*;

import java.awt.event.*;

import java.applet.*;

public class JPoly extends JApplet implements

ActionListener { JPanel p = new JPanel();

JPolyCanvas c = new JPolyCanvas(20,50);

JRadioButton c1 = new JRadioButton("Start");

public void init() {

Container cont = getContentPane();

cont.setLayout(new BorderLayout());

c1.addActionListener(this);

p.add(c1);

cont.add("South",p);

cont.add("Center",c);

setVisible(true);

}

public void actionPerformed(ActionEvent e) { if (e.getSource() == c1 &&

c1.getText().equals("Stop")) {

c.stop(); c1.setText("Start");

}

else if (e.getSource() == c1) { c.start(); c1.setText("Stop");

} }

public void stop() { c1.setText("Start");

c.stop();

}

public void start() { c1.setText("Stop");

c.start();

}

public void destroy() { c.destroy();

}

public static void main(String[] args)

Threads 30 January 2003 44

{

JPoly p = new JPoly();

} }

This will result in

(12)

Threads 30 January 2003 45

We can also do something more fun

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

public class JPolyMany extends JApplet { JPolyCanvas c[];

public void init() {

Container cont = getContentPane();

c = new JPolyCanvas[4];

c[0] = new JPolyCanvas(20,40);

c[1] = new JPolyCanvas(100,40);

c[2] = new JPolyCanvas(30,50);

c[3] = new JPolyCanvas(5,40);

cont.setLayout(new GridLayout(2,2));

cont.add(c[0]);

cont.add(c[1]);

cont.add(c[2]);

cont.add(c[3]);

c[0].start();

c[1].start();

c[2].start();

c[3].start();

} }

Threads 30 January 2003 46

Which will produce

Images and sounds 30 January 2003 47

Images and sounds

Images can be loaded in both Applets and standalone programs, but different procedures are used.

The Applet class have two methods for this purpose,

getCodeBase(), class file location getDocumentBase(), html file location They both return a URL-object that can be used.

Images and sounds 30 January 2003 48

To load an image (a gif or a jpeg) you use the getImage method.

Image im;

im = getImage(getCodeBase(),”filename”);

The image must be stored in the same

catalogue as the class file, or in a sub-

catalogue. You cannot use an absolute

path in the filename.

(13)

Images and sounds 30 January 2003 49

To show the image, the drawImage method can be used.

g.drawImage(im, xstart, ystart, this);

or to rescale the image

g.drawImage(im, xstart, ystart, xsize, ysize, this);

The last parameter is an ImageOb- server. This is an interface imple- mented by all Component objects.

g is a Graphics object.

Images and sounds 30 January 2003 50

The getImage is asynchronous and returns immediately and the image is loaded in the background. The obser- ver automatically monitors the load- ing process and displays the image as it is loaded.

There are tools to handle this process manually too.

Images and sounds 30 January 2003 51

A simple example

import java.applet.*;

import java.awt.*;

public class ImDemo extends Applet { Image image;

public void init() {

// get filename from the html-file String name = getParameter("imagename");

image = getImage(getCodeBase(), name);

}

public void paint(Graphics g) { g.drawImage(image,10,10,this);

} }

And the corresponding HTML-file

<html>

<head>

<title>ImDemo</title>

</head>

<body>

<applet code=ImDemo.class width=300 height=200 align=right>

<param name=imagename value=olle.gif>

</applet>

<H1> Welcome to <br>

Java</H1>

</body> </html>

Images and sounds 30 January 2003 52

The result is

To get the size of the Image there are two methods available

getWidth(ImageObserver o) getHeight(ImageObserver o) both are methods in the Image class.

The ImageObserver is usually given as

this.

(14)

Images and sounds 30 January 2003 53

If it is vital that the Image is completely loaded before using it you need to use some synchronization tools, like the MediaTracker

You can do

MediaTracker mt =

new MediaTracker(this);

to create it. Then you must set it up mt.addimage(image, id);

This will add the Image image to its supervisor list and give it the idnum- ber id. Each image should have a unique id in your program.

Images and sounds 30 January 2003 54

Then you wait for it to load try {

mt.waitForID(id);

} catch ( InterruptedException e) {}

Images and sounds 30 January 2003 55

In a program this gets

import java.applet.*;

import java.awt.*;

public class ImDemo2 extends Applet { Image image;

MediaTracker mt;

public void init() { mt = new MediaTracker(this);

String name = getParameter("imagename");

image = getImage(getCodeBase(), name);

mt.addImage(image,0);

try { mt.waitForID(0);}

catch ( InterruptedException e) {}

}

public void paint(Graphics g) { g.drawImage(image,10,10,this);

} }

Note that this makes the program less efficient and it shouldn’t be used unless you actually need to access the image for other purposes than to dis- play it.

Images and sounds 30 January 2003 56

To load an Image in a standalone pro- gram a different technique is used You still need an URL but there is no CodeBase. So the URL has to be con- structed in another way.

You do that in the following way URL u = new URL(“a URL”);

Then you can load the contents of the URL using

ImageProducer ip =

(ImageProducer) u.getContent();

This requires that it is an image you

are loading. If the URL refers some-

thing else the typecast will fail.

(15)

Images and sounds 30 January 2003 57

To get an Image from the ImagePro- ducer you do

Image im = createImage(ip);

If you have a local image file you can use an alternative approach,

Image im =

Toolkit.GetDefaultToolkit().createImage(

“filename”);

Images and sounds 30 January 2003 58

A small program that uses this is

import java.awt.*;

import java.awt.image.*;

import java.net.*;

import java.io.*;

public class BildApp extends Frame { URL u; Image bilden;

/**

* Constructor.

*/

public BildApp (String namn) { try {

u = new URL(namn);

}

catch (MalformedURLException e) {

System.out.println("Illegal URL " + namn);

} try {

bilden =

createImage((ImageProducer) u.getContent());

}

catch (IOException e) {

System.out.println("Error when reading " + namn);

}

setSize(400,400);

setTitle(u.getHost() + u.getFile());

setVisible(true);

}

public void paint(Graphics g) {

g.drawImage(bilden, 50, 50, 300, 300, this);

}

public static void main(String [] arg) { new BildApp(arg[0]);

} }

Images and sounds 30 January 2003 59

If you do

java BildApp http://www.cs.chalmers.se/~skanshol/Java_dir/hund.gif

you will get

Images and sounds 30 January 2003 60

The best way to integrate an Image into a graph is to extend a Canvas or JPanel and to load the Image into the class.

The class can then easlily added to a Container.

You have the choice if the image

should be scaled or truncated if it is too

big.

(16)

Images and sounds 30 January 2003 61

Animation is achieved by loading a number of images and displaying them as fast as possible.

A simple piece of code that do anima- tion is

Images and sounds 30 January 2003 62

import java.awt.*;

import java.awt.image.*;

import java.net.*;

import java.io.*;

public class Animator extends Frame implements Runnable { private URL u;

private Image [] bilden; // An array of images private int index = 0;

private Thread t;

private int size;

/**

* Constructor.

*/

public Animator (String[] name) { bilden = new Image[name.length];

size = name.length;

// load the images

for (int i = 0; i < name.length; i++) { try {

u = new URL(name[i]);

}

catch (MalformedURLException e) {

System.out.println("Illegal URL " + name[i]);

} try { bilden[i] =

createImage((ImageProducer) u.getContent());

}

catch (IOException e) { System.out.println(

"Error when reading " + name[i]);

} }

t = new Thread(this);

t.start();

setSize(400,400);

setTitle(u.getHost() + u.getFile());

setVisible(true);

}

// the display thread public void run() {

while (true) { try {

Images and sounds 30 January 2003 63

Thread.sleep(100);

}

catch (InterruptedException e) {}

index = (index + 1) % size;

repaint();

} }

public void paint(Graphics g) {

g.drawImage(bilden[index], 50, 50, 300, 300, this);

}

public static void main(String [] arg) { new Animator(arg);

} }

This requires that the images have the same size. We don’t use a MediaT- racker to check the loading, that might give some slowdown in the beginning of the film.

Images and sounds 30 January 2003 64

Sounds are easy to handle, the only requirement is that you have sound hardware. Java 1.2 supports the AIFF, WAW, TYPE 0 MIDI and TYPE 1 MIDI formats.

To load a soundfile into an Applet is very easy

AudioClip a =

getAudioClip(getCodeBase(),”soundfile”);

Then there are three simple methods available

play(), play the sound

stop(), stop the play

loop(), play in a loop

(17)

Images and sounds 30 January 2003 65

A simple program might be

import java.awt.*;

import java.awt.event.*;

import java.applet.*;

public class SoundDemo extends Applet { AudioClip frogsound;

Image frogimage;

MediaTracker mt = new MediaTracker(this);

// draw the image and play the sound public void paint(Graphics gr) {

gr.drawImage(frogimage,50,50,this);

frogsound.play();

}

// add a mouse listener, each time a mousebutton // is pressed, play the sound

MouseListener ml = new MouseAdapter () { public void mouseClicked(MouseEvent e) {

frogsound.play();

} };

// set up things public void init() {

addMouseListener(ml);

frogsound = getAudioClip(getCodeBase(),"frog.au");

frogimage = getImage(getCodeBase(),"frog.gif");

mt.addImage(frogimage,0);

try {

mt.waitForID(0);

}

catch (InterruptedException e) {}

frogsound.play();

resize(300,400);

} }

Images and sounds 30 January 2003 66

Will give you this

Images and sounds 30 January 2003 67

To use sounds in a standalone applica- tion you need a URL. In addition you need a Java2 implementation.

URL u = new URL(“a URL”);

To load the file you do AudioClip a =

Applet.newAudioClip(u);

This is a static method in the Applet class.

Images and sounds 30 January 2003 68

An example would be

import java.awt.*;

import java.awt.image.*;

import java.awt.event.*;

import java.net.*;

import java.applet.*;

import java.io.*;

public class SoundDemo2 extends Frame { // create the things we need

private AudioClip frogsound;

private URL u1, u2;

private Image frogimage;

private MediaTracker mt = new MediaTracker(this);

// draw the image, play the sound public void paint(Graphics gr) {

gr.drawImage(frogimage,50,50,this);

frogsound.play();

}

// add a mouselistener

MouseListener ml = new MouseAdapter () { public void mouseClicked(MouseEvent e) {

frogsound.play();

} };

// constructor public SoundDemo2() { addMouseListener(ml);

// try to load the image and the sound, create URL’s try {

u1 = new URL(

"http://www.tdb.uu.se/~olle/pvk/assorted/frog.gif");

u2 = new URL(

"http://www.tdb.uu.se/~olle/pvk/assorted/frog.au");

}

catch (MalformedURLException e) { System.out.println("Illegal URL");}

(18)

Images and sounds 30 January 2003 69

// read the soundfile and the imagefile

frogsound = Applet.newAudioClip(u2);

try { frogimage =

createImage((ImageProducer) u1.getContent());

}

catch (IOException e) {

System.out.println("Error when reading the image");

}

// wait for the image to load mt.addImage(frogimage,0);

try {

mt.waitForID(0);

}

catch (InterruptedException e) {}

setSize(300,400);

setVisible(true);

}

public static void main(String [] arg) { SoundDemo2 s = new SoundDemo2();

} }

Images and sounds 30 January 2003 70

We have seen ImageObserver and ImageProducer here. There are also an ImageConsumer interface

If you are interested in modifying the image in any way you should declare an ImageConsumer, modify the data, recreate the image using an ImagePro- ducer.

Images and sounds 30 January 2003 71

One implementation of ImageCon- sumer is the PixelGrabber.

int [] arr = new int[width][height];

PixelGrabber pg =

new PixelGrabber(Image, x, y, w, h, int [] arr, offset, scanset);

pg.grabPixels();

You now can work with the array. It contains 32 bit pixels, 4x 8bit for Alpha, R, G and B

Images and sounds 30 January 2003 72

Once you are finished you can do Image im;

im = createImage

(new MemoryImageSource(

width,height, array, 0, width));

The MemoryImageSource implements

the ImageProducer Interface.

(19)

Images and sounds 30 January 2003 73

But you can also declare a BufferedIm- age.

Such an object allows you to access data directly without all the help- classes.

If you have a loaded Image your can convert it to a BufferedImage using something like

Images and sounds 30 January 2003 74

// assume the image is a loaded Image

BufferedImage bilden = new BufferedImage(image.getWidth(null), image.getHeight(null),

BufferedImage.TYPE_INT_RGB);

Graphics2D g2 = bilden.createGraphics();

g2.drawImage(image,null,null);

This creates the image, obtains its Graphics2D object and renders the Image into the BufferedImage.

Images and sounds 30 January 2003 75

You can then do

for (int i =0; i < bilden.getWidth(); i++) for (int j=0; j < bilden.getHeight(); j++)

bilden.setRGB(i, j, bilden.getRGB(i,j) & 0xFFFF00FF);

The getRGB method gives you pixel packed as 4 8-bit pixels in an int, the order is Alpha, Red, Green, Blue.

This piece of code clears out the green part of all pixels

Images and sounds 30 January 2003 76

A piece of code the blurs the image with a convolution

BufferedImage b;

float ninth = 1.0f/ 9.0f;

float [] blurKernel = { ninth, ninth, ninth, ninth, ninth, ninth,

ninth, ninth, ninth};

ConvolveOp blurOp = new ConvolveOp ( new Kernel(3, 3, blurKernel),

ConvolveOp.EDGE_NO_OP, null);

b = blurOp.filter(bilden,null);

This creates a 3x3 matrix that is used to

do a convolution kernel. We ignore the

edge pixels and have no rendering

hints. Finally the operation is done.

(20)

Images and sounds 30 January 2003 77

Another piece of code

BufferedImage b;

short [] invert = new short[256];

short [] straight = new short[256];

for (int i = 0; i < 256; i++) { invert[i] = (short)(255-i);

straight[i] = (short) i;

}

short[] [] redInvert = { invert, straight, straight};

LookupTable table = new ShortLookupTable(0,redInvert);

LookupOp redinvertOp = new LookupOp(table,null);

b = redinvertOp.filter(bilden,null);

This creates a lookup table that is used to transform each pixel. We create three of these, one for each color. The RED one inverts the 8-bits scale, the GREEN and the BLUE do nothing.

Images and sounds 30 January 2003 78

A rotation

BufferedImage b;

AffineTransform at =

AffineTransform.getRotateInstance(Math.PI/6,0,80);

RenderingHints rh = new RenderingHints(

RenderingHints.KEY_INTERPOLATION,

RenderingHints.VALUE_INTERPOLATION_BILINEAR);

AffineTransformOp rotateOp = new AffineTransformOp(at,rh);

b = rotateOp.filter(bilden,null);

This creates a rotation around the point (0,80) of pi/6. We do want bilinear interpolation during the operation.

Images and sounds 30 January 2003 79

A complete code

import javax.swing.*;

import java.awt.*;

import java.awt.geom.*;

import java.awt.image.*;

import java.net.*;

import java.io.*;

public class JBildApp extends JFrame { BufferedImage bilden;

Image image;

/**

* Constructor.

*/

public JBildApp (String namn) { // load the image

MediaTracker mt = new MediaTracker(this);

image =

Toolkit.getDefaultToolkit().getImage(namn);

// wait for it to load mt.addImage(image,0);

try { mt.waitForID(0);}

catch ( InterruptedException e) {}

// create a BufferedImage

bilden = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);

// create Graphics2D

Graphics2D g2 = bilden.createGraphics();

// draw the image into the BufferedImage g2.drawImage(image,null,null);

setSize(400,400);

setVisible(true);

}

Images and sounds 30 January 2003 80

// Clear out the green part of the image public void clear_G() {

for (int i =0; i < bilden.getWidth(); i++) for (int j=0; j < bilden.getHeight(); j++)

bilden.setRGB(i, j,

bilden.getRGB(i, j) & 0xFFFF00FF);

repaint();

}

// blur the image with a convolution kernel public void convolve() {

BufferedImage b; // work image // create the matrix

float ninth = 1.0f/ 9.0f;

float [] blurKernel = { ninth, ninth, ninth, ninth, ninth, ninth, ninth, ninth, ninth};

// create the kernel

ConvolveOp blurOp = new ConvolveOp (

new Kernel(3, 3, blurKernel), ConvolveOp.EDGE_NO_OP, null);

// apply

b = blurOp.filter(bilden,null);

// swap the image bilden = b;

repaint();

}

(21)

Images and sounds 30 January 2003 81

// rotate

public void rotate() { BufferedImage b;

// create an affine transformation AffineTransform at =

AffineTransform.getRotateInstance(Math.PI/6,0,80);

// create the rendering hint RenderingHints rh = new RenderingHints(

RenderingHints.KEY_INTERPOLATION,

RenderingHints.VALUE_INTERPOLATION_BILINEAR);

// create the transformation AffineTransformOp rotateOp =

new AffineTransformOp(at,rh);

// apply it

b = rotateOp.filter(bilden,null);

bilden = b;

repaint();

}

// invert the red public void invert_R() {

BufferedImage b;

short [] invert = new short[256];

short [] straight = new short[256];

// create the lookup tables for (int i = 0; i < 256; i++) { invert[i] = (short)(255-i);

straight[i] = (short) i;

}

// a common transform short[] [] redInvert =

{ invert, straight, straight};

// create the final table LookupTable table =

Images and sounds 30 January 2003 82

new ShortLookupTable(0,redInvert);

// and the transform

LookupOp redinvertOp = new LookupOp(table,null);

// apply it

b = redinvertOp.filter(bilden,null);

bilden = b;

repaint();

}

public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g;

g2.drawImage(bilden, null, 20,50);

}

public static void main(String [] arg)

throws InterruptedException { JBildApp a = new JBildApp(arg[0]);

Thread.sleep(2000);

a.invert_R();

Thread.sleep(2000);

a.convolve();

Thread.sleep(2000);

a.rotate();

Thread.sleep(2000);

a.clear_G();

} }

Images and sounds 30 January 2003 83

If you do

java JBildApp olle.gif You will end up with

Arrays, Strings and Collections 30 January 2003 84

Arrays, Strings and Collections Java has some basic data structures.

The simplest of these are String, StringBuffer and the array.

The String class is used to represent a number of characters.

It has a lot of methods, some are length(), the length of the String charAt(n), the n’th character substring(n,m), substring n..m-1 compareTo(s), compare Strings equals(s), test if equal

indexOf(c), index of the character c

trim(), remove leading/trailing spaces

valueOf(x), convert x into a String

(22)

Arrays, Strings and Collections 30 January 2003 85

Note that the test for equality is equals, not the == operator.

String s = “Nisse”;

String t = “Nisse”;

if (s.equals(t))

System.out.println(“Equal”);

else

System.out.println(“Not equal”);

if (s == t)

System.out.println(“Equal”);

else

System.out.println(“Not equal”);

The first one will print “Equal”. The second one compares the references, i. e. whether s and t points to the same string. The result of this is probably

“Not equal” but its up to compiler.

Arrays, Strings and Collections 30 January 2003 86

A String object is ReadOnly! There is no way to change the content of a String.

You may of course throw away the object and create a new object with a different content. You may also put Strings together using the +-operator but that also creates a new object.

There is also a class StringBuffer with objects that can be modified.

Some additional methods in String- Buffer are:

append(s), append to the StringBuf.

insert(pos,s), insert s at position pos

Arrays, Strings and Collections 30 January 2003 87

String and StringBuffer implement the interface Comparable, i. e. they imple- ment the method compareTo, and they can be sorted.

The sorting is based on the order of the character codes. This means that other languages than English might get incorrectly sorted.

Arrays, Strings and Collections 30 January 2003 88

To handle this, a more general ordering mechanism is available, the Collator class.

It uses the current locale and compares using the local rules. It can also be set to different strength.

In English we have:

a and b is a PRIMARY difference.

a and å is a SECONDARY difference a and A is a TERTIARY difference a and a are IDENTICAL

å is no letter in English but a variant of the letter a, a diacritical.

It can be used like this

(23)

Arrays, Strings and Collections 30 January 2003 89

Collator co = Collator.getInstance();

// TERTIARY is default if (co.compare(“åska”,”Äska”) < 0) System.out.println(“åska is before Äska”);

else if (co.compare(“åska”,”Äska”) > 0) System.out.println(“åska is after Äska”);

else

System.out.println(“equal”);

co.setStrength(Collator.SECONDARY);

if (co.compare(“åska”,”Äska”) < 0) System.out.println(“åska is before Äska”);

else if (co.compare(“åska”,”Äska”) > 0) System.out.println(“åska is after Äska”);

else

System.out.println(“equal”);

co.setStrength(Collator.PRIMARY);

if (co.compare(“åska”,”Äska”) < 0) System.out.println(“åska is before Äska”);

else if (co.compare(“åska”,”Äska”) > 0) System.out.println(“åska is after Äska”);

else

System.out.println(“equal”);

The output will be

åska is before Äska åska is before Äska equal

Arrays, Strings and Collections 30 January 2003 90

If you instead do

Collator co = Collator.getInstance(new Locale(“sv”,”SE”));

The output will be

åska is before Äska åska is before Äska åska is before Äska

Why? Because in Swedish, å and ä make up a PRIMARY difference since they are distinc letters, in English they have a SECONDARY difference.

Arrays, Strings and Collections 30 January 2003 91

If you do a lot of comparisons using the same string it might be a good idea to compute a CollationKey. It is a key that is fast to compare that reflects the order of the strings.

CollationKey k1 = co.getCollatorKey(“program”);

CollationKey k2 = co.getCollatorKey(“pâté”);

if (k1.compareTo(k2) < 0)....

Arrays, Strings and Collections 30 January 2003 92

All arrays are objects, even though the elements may not be (i. e. ints).

They have a public instance variable length, that gives the size of the array.

A declaration int [] a;

double [][] b;

just declares a reference, not an array.

(24)

Arrays, Strings and Collections 30 January 2003 93

Whereas

a = new int[100];

b = new double[10][10];

creates the array

Arrays, Strings and Collections 30 January 2003 94

A multidimensional array is really a reference to an array of references to an array. Thus you could do

b = new double[10][];

for (int i = 0; i < b.length; i++) b[i] = new int[10];

The length of b is the number of lines, the number of columns is b[0].length Note that this makes it possible to do matrices that are nonrectangular. e. g.

b = new double[10][];

for (int i =0 ; i < b.length; i++) b[i] = new int[i+1];

Arrays, Strings and Collections 30 January 2003 95

Arrays of objects can of course also be created. The array will be an array of references, not an array of objects though. Assuming that we have a class, Person, we can create an array:

Person [] parr;

parr = new Person[10];

for (int i=0; i < 10; i++) parr[i] = new Person();

Note the separate step to create the objects!

Arrays, Strings and Collections 30 January 2003 96

Arrays may be compared using the ==

operator but that just compare the ref- erence (the addresses), not the ele- ments.

The assignment operator just copy the reference, not the elements.

To copy an array, you either use arrayc-

opy or clone.

(25)

Arrays, Strings and Collections 30 January 2003 97

Arraycopy looks like

System.arraycopy(fromarray, startindex, toarray, startindex, numberofelem);

The outputarray must have been ini- tialized, i. e. must not be a nullrefer- ence and it must be large enough to hold the input data.

Arraycopy is shallow which means that it just copies the array. If you have an array of objects, the objects will not be copied, just the references.

Arrays, Strings and Collections 30 January 2003 98

Another method is clone, int [] a = new int [100];

int [] b;

b = (int []) a.clone();

The clone method returns an Object and must be typecasted back to the cor- rect type. The destination need not exist to use clone.

This is also a shallow copy.

Arrays, Strings and Collections 30 January 2003 99

If you have

int [][] a = new int [2][3];

int [][] b;

you must do

b = (int [][]) a.clone();

for (int i = 0; i < a.length; i++) b[i] = (int []) a[i].clone();

to copy the matrice deep.

Arrays, Strings and Collections 30 January 2003 100

If you have

Person [] p = new Person[10];

for (int i=0; i < 10; i++) p[i] = new Person();

Person [] q;

you must do

q = (Person []) p.clone();

for (int i = 0; i < 10; i++) q[i] = (Person) p[i].clone();

to get a deep copy.

(26)

Arrays, Strings and Collections 30 January 2003 101

A class cannot in general do cloning, to be able to do that you need to:

implement the empy interface Clonea- ble

optionally override the clone method in the Object class. If you select not to override this method, a shallow copy will be made.

If shallow copying is OK public class Person

implements Cloneable { will do.

If you want deep cloning, you should override the clone method.

public Object clone()

Arrays, Strings and Collections 30 January 2003 102

There is a package

java.util.Arrays (in Java2 only) that contains some useful methods equals(a,b), compare two arrays sort(a), sort an array

binarySearch(a,k), seach for k in a Sorting requires that the elements have a natural ordering or that they imple- ments the Comparable interface. This interface defines the methodcom- pareTo that should be implemented.

Arrays, Strings and Collections 30 January 2003 103

Other data structures

Java2 defines a number of builtin data structures. These are declared by a number of interfaces and a number of implementations of these interfaces.

The basic interface is Collection. This interface have two subinterfaces called List and Set. The Set interface have a subinterface SortedSet.

Arrays, Strings and Collections 30 January 2003 104

A List is an ordered (not neccesarily sorted) structured sequence of ele- ments. Each element has a position.

The same value can occur several times

A Set is an unordered structure where

a value occurs once (or not at all).

(27)

Arrays, Strings and Collections 30 January 2003 105

Lists are implemented by Vector, ArrayList and LinkedList.

Sets are implemented by HashSet and TreeSet (a SortedSet).

Since Collection is an interface you can implement your own structure if you like to.

Arrays, Strings and Collections 30 January 2003 106

A Collection defines some methods add(Object o)

clear() isEmpty() iterator()

remove(Object o)

It also defines a constructor that takes any Collection as parameter and that converts that Collection to the current structure.

Arrays, Strings and Collections 30 January 2003 107

An Iterator is a modernized version of Enumerator and defines methods like hasNext, next, remove.

The List interface defines some addi- tional methods (page 637 in Skans- holm), in addition it uses a List-Iterator that is more specific than theIterator.

Arrays, Strings and Collections 30 January 2003 108

There are also maps. A map is a struc- ture where access data using a key.

There are the classes TreeMap, Hashta- ble and HashMap implemented.

See Skansholm chapter 19.4

(28)

Arrays, Strings and Collections 30 January 2003 109

A final example, a program that reads words from a file and inserts them into a binary searchtree.

import java.util.*;

import java.text.*;

import java.io.*;

import extra.*;

public class TextAnalys {

// detta är ett program. övar lite, testa om ord som // åke, östen och sånt sorteras rätt

public static void main(String [] arg) { String word;

Collator co =

Collator.getInstance(new Locale("sv","SE"));

co.setStrength(Collator.PRIMARY);

TreeSet words = new TreeSet(co);

// specify sorter

// ExtendedReader is a Skansholm class ExtendedReader

r = ExtendedReader.getFileReader(arg[0]);

// read one word at a time add it to the tree while ((word = nextWord(r)) != null) words.add(word.toUpperCase());

// print all words

for (Iterator i = words.iterator(); i.hasNext();) Std.out.println(i.next());

}

public static String nextWord(ExtendedReader r) { String s = "";

int c;

// skip nonletters

while ((c = r.lookAhead()) != -1 &&

!Character.isLetter((char) c)) c = r.readChar();

Arrays, Strings and Collections 30 January 2003 110

// get the letters

while ((c = r.lookAhead()) != -1 &&

Character.isLetter((char) c)) s = s + (char) r.readChar();

if (s.length() == 0) return null;

else return s;

} }

If you run it like

java TextAnalys TextAnalys.java You will get

AADD ALLARG AT CCHAR CHARACTER CLASS CO COLLATOR DETTA ELSEETT

EXTENDEDREADER EXTRA FOR GETGETFILEREADER GETINSTANCE HASNEXT IIF IMPORT INT IOIS ISLETTER IT

Arrays, Strings and Collections 30 January 2003 111

ITERATOR JAVALENGTH LETTERS LITELOCALE LOOKAHEAD MAIN NEWNEXT NEXTWORD NONLETTERS NULLOCH OMONE ORDOUT PRIMARY PRINT PRINTLN PROGRAM PUBLIC R READREADCHAR RETURN RÄTTS SESETSTRENGTH SKANSHOLM SKIPSOM SORTER SORTERAS SPECIFY STATIC STD STRING SVSÅNT TESTA TEXT TEXTANALYS THETIME TOTOUPPERCASE TREE

Arrays, Strings and Collections 30 January 2003 112

TREESET UTILWHILE VOIDWORD WORDS ÅKEÄR ÖSTEN ÖVAR

References

Related documents

We feel that IT governance plays a determinant role in developing effective key performance indicators to measure sustainability in supply chain management as well as in allocation

One point more than 3.00 sigmas from center line... One point more than 3.00 sigmas from

In section 3.2 ‘Building a culture of peace in schools affected by conflict’, I discuss the role education can play in building peace and fuelling conflict, with

Expanding comparative effectiveness research in the United States is essential to provide reliable data on the risks and benefits of health interventions, so that this information

Flash flood EWSs are complex systems, and so this chapter explains the purpose of a ConOps within the context of the “system engineering life cycle process”.. It then lists the

Following basic training in technology, the programme explores specific management issues in greater depth, particularly in order to develop the skills required to analyse

• The number of car accidents caused by faulty brakes, like the number caused by faulty wiring, have increased significantly since regulations on manufacturing were relaxed9. •

REVISED Due Dates, 2020 TTI 1 Strategic plan basics 1 st Draft of Plan due To SPA team March 2-5 Webinar Needs assessment March 24 May 15 [email protected] 2020 SP