// RandomWalk: test an applet with threads to carry out // random walks. Can suspend and resume import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class RandomWalk extends Applet implements Runnable, ActionListener { public Queue q = new Queue(500); // queue in sep class private final static int SIZE = 8; // # of threads private TextField[] output; // field for each thread private Button[] button; // button for each thread private Walk[] walk; // the array of threads private Color[] colors = {Color.blue, Color.pink, Color.green, Color.yellow,Color.orange, Color.magenta, Color.gray, Color.cyan}; private Thread painting; // thread to paint every 0.2 sec private ActiveThreads a = new ActiveThreads(); private TextField threadCount; // display activeThreads private int h; // height and width private int d; // d = h/t is increment amount private int t; // h/d is increment amount public void init() { setBackground(Color.black); output = new TextField[SIZE]; button = new Button[SIZE]; walk = new Walk[SIZE]; Font boldFont = new Font("Bold", Font.BOLD, 11); for (int i = 0; i < SIZE; i++) { output[i] = new TextField(" " + i); output[i].setEditable(false); output[i].setBackground(colors[i % colors.length]); output[i].setFont(boldFont); button[i] = new Button("s/r " + i); button[i].setBackground(Color.white); button[i].addActionListener(this); button[i].setFont(boldFont); add( output[i] ); add( button[i] ); } threadCount = new TextField(); threadCount.setEditable(false); threadCount.setBackground(Color.yellow); threadCount.setFont(boldFont); add(threadCount); } public void start() { // create threads and start every time start is called h = 800; t = 4; d = h/t; for (int i = 0; i < SIZE; i++) { walk[i] = new Walk(h, d, 40, 410, 420, q, a); walk[i].start(); a.incrActiveThreads(); } threadCount.setText(a.getActiveThreads() + " threads"); painting = new Thread(this); painting.start(); } public synchronized void stop() { // stop threads every time stop is called // as the user browses another Web page for (int i = 0; i < walk.length; i++) { walk[i] = null; } notifyAll(); } public synchronized void actionPerformed(ActionEvent e) { for (int i = 0; i < button.length; i++) { if ( e.getSource() == button[i] ) { if (walk[i].getSuspend()) { output[i].setBackground(colors[i % colors.length]); // this synchonized on same thread that waits synchronized(walk[i]) { walk[i].resetSuspend(); walk[i].notifyAll(); a.incrActiveThreads(); } } else { output[i].setBackground(Color.red); walk[i].setSuspend(); a.decrActiveThreads(); } if (a.getActiveThreads() == 1) threadCount.setText(a.getActiveThreads() + " thread"); else threadCount.setText(a.getActiveThreads() + " threads"); return; } } } public void paint(Graphics g) { int x, y; Entry s = new Entry(); // remove paint requests from queue while (true) { s = q.removeRequest(); if (s == null) break; x = s.locX; y = s.locY; g.setColor( colors[s.threadNum % colors.length] ); // g.fillOval(x, y, 5, 5); g.fillRect(x, y, h/d, h/d); } } public void update(Graphics g) { // prevent background clearing paint(g); } // a thread to paint every 0.2 seconds public void run() { while (true) { // sleep 0.2 seconds try { Thread.sleep( 200 ); } catch (InterruptedException e) { e.printStackTrace(); } repaint(); } } } // ~/java_sdk/jdk1.5.0_08/bin/appletviewer RandomWalk.html // ~/java_sdk/jdk1.5.0_08/bin/javac RandomWalk.java //------------------------------------------------------------ // Walk: carries out 1 random walk public class Walk extends Thread { private int x; // current x coord of walk private int y; // current y coord of walk private boolean suspend; // true means thread waits private int h; // height and width private int d; // h/d is increment amount private int t; // size of initial vertical section private int threadNum; // each thread numbered from 0 private static int num = 0; // used to number threads private Queue q; // separate queue, for paint requests private ActiveThreads a; // tell # of active threads public void setSuspend() { // thread should wait suspend = true; } public void resetSuspend() { // applet should notify it suspend = false; } public boolean getSuspend() { // needed by applet return suspend; } // constructor, last 2 params make the queue & number // of thread available to this class public Walk(int hc, // height and width int dc, // h/d is increment amount int tc, // size of initial vertical section (??) int xc, int yc, // x, y coords of walk Queue qc, // the queue ActiveThreads ac) { // number of active threads h = hc; d = dc; t = tc; x = xc; y = yc; q = qc; a = ac; threadNum = num++; } public void run() { while (true) { int act = a.getActiveThreads(); if (act < 1) act = 1; // sleep 0 to 0.8 secs, faster with fewer threads try { sleep((int) ( Math.random() * 100 * act)); synchronized(this) { while (suspend) wait(); } } catch (InterruptedException e) { e.printStackTrace(); } int r = (int)(8*Math.random()); // advance position; all 8 directs equally likely // bounce off the boundary if (r == 1 || r == 0 || r == 7) x += h/d; else if (r == 3 || r == 4 || r == 5) x -= h/d; if (x < 10) x += h/d; if (x > h) x -= h/d; if (r == 1 || r == 2 || r == 3) y += h/d; else if (r == 5 || r == 6 || r == 7) y -= h/d; if (y < t) y += h/d; if (y > h + t) y -= h/d; q.addRequest(threadNum, x, y); // would like to paint here, but don't know how } // while } // run // public static void main(String[] args) { // Thread th1 = new Walk(400, 100, 200, 200, 400); // Thread th2 = new Walk(400, 100, 200, 200, 400); // System.out.println("Let's get started"); // th1.start(); // th2.start(); // } } //------------------------------------------------------------ // Queue: array-based circular queue // used to hold requests to paint points public class Queue { private int QSIZE = 500; // default size (was 50) private Entry[] q; // array for queue private int rp = 0, fp = 0; // rear and front private int qs = 0; // current # of entries public Queue() { // default constructor q = new Entry[QSIZE]; for (int i = 0; i < QSIZE; i++) q[i] = new Entry(); } public Queue(int qSize) { // any size QSIZE = qSize; q = new Entry[QSIZE]; for (int i = 0; i < QSIZE; i++) q[i] = new Entry(); } public synchronized void addRequest(int i, int x, int y) { if (qs == QSIZE) { System.err.println("Overflow in queue"); System.exit(1); } qs++; rp = (rp + 1)%QSIZE; q[rp].threadNum = i; q[rp].locX = x; q[rp].locY = y; } public synchronized Entry removeRequest() { Entry r = new Entry(); if (qs == 0) { return null; } qs--; fp = (fp + 1)%QSIZE; r.threadNum = q[fp].threadNum; r.locX = q[fp].locX; r.locY = q[fp].locY; return r; } // main here only to test Queue class public static void main(String[] args) { Entry e; Queue q = new Queue(); int n = Integer.parseInt(args[0]); for (int i = 0; i < n; i++) { q.addRequest(i, i, i); } for (int i = 0; i < n; i++) { e = q.removeRequest(); System.out.print(e.threadNum + " "); } System.out.println(); Queue r = new Queue(10); for (int i = 0; i < n; i++) { r.addRequest(i, i, i); } for (int i = 0; i < n; i++) { e = r.removeRequest(); System.out.print(e.threadNum + " "); } System.out.println(); } } //------------------------------------------------------------ // Entry: class used for Queue entries // these are requests to paint a point public class Entry { int threadNum; // number used to decide color int locX; // x-coord to paint int locY; // y-coord to paint } //------------------------------------------------------------ // ActiveThreads: tell classes how many // threads are active public class ActiveThreads { private int num = 0; // # of active threads public void incrActiveThreads() { num++; } public void decrActiveThreads() { num--; } public int getActiveThreads() { return num; } }