1

I've been working on this for awhile and can't figure out why this applet is not working properly:

I instantiated two threads in my applet. I created two buttons- start, and stop, which are supposed to change the flag values to end the while() loops in my threads. The applet is not responding to either button. Any suggestions, anyone? Thanks for your time!

This is the applet...

package prodcons;

import java.applet.Applet;
import java.util.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class MyTableSetting extends Applet {
Soup s;                                             // we will show     the soup bowl with the soup's alphabet pieces
int bowlLength = 150;                               // bowl's dimensions as variables in case we want to change it
int bowlWidth = 220;
int bowlX = 60;
int bowlY = 10;
Producer p1;
Consumer c1;



public void init(){
    setSize(400,200);                                 // make the applet size big enough for our soup bowl
    s = new Soup();                                   // instantiate the Soup
    p1 = new Producer(this, s);              // declare and instantiate one producer thread - state of NEW
    c1 = new Consumer(this, s);              // declare and instantiate one consumer thread - state of NEW
    p1.start();                                       // start the producer thread
    c1.start();                                       // start the consumer thread

    Button stop = new Button("Stop");
    stop.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            Producer.producerRunning = false;
            Consumer.consumerRunning = false;
            Soup.clearBuffer();
        }
    });
    add(stop);

    Button start = new Button("Start");
    start.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            Producer.producerRunning = true;
            Consumer.consumerRunning = true;
            p1.run();
            System.out.println("heyyy");
            c1.run();
        }
    });
    add(start);
}

public void paint(Graphics g){                        // first we make the bowl and spoon
    int x;
    int y;
    g.setColor(Color.orange);
    g.fillOval(bowlX, bowlY, bowlWidth, bowlLength);  // the bowl
    g.setColor(Color.cyan);
    g.fillOval(10, 25, 40, 55);                       // the spoon
    g.fillOval(25, 80, 8, 75);
    g.setColor(Color.black);                          // black outlines for the dinnerware
    g.drawOval(10, 25, 40, 55);
    g.drawOval(25, 80, 8, 75);
    g.drawOval(bowlX,bowlY, bowlWidth, bowlLength);
    ArrayList <String> contents = s.getContents();  // get contents of the soup
    for (String each: contents){                      // individually add each alphabet piece in the soup
        x = bowlX + bowlWidth/4 +(int)(Math.random()* (bowlWidth/2));  // put them at random places to mimic stirring
        y = bowlY + bowlLength/4 + (int)(Math.random()* (bowlLength/2));
        Font bigFont = new Font("Helvetica", Font.BOLD, 20);
        g.setFont(bigFont);
        g.drawString(each, x, y);
    }
}   

}

and these are the threads:

package prodcons;

class Consumer extends Thread {
private Soup soup;
private MyTableSetting bowlView;
static boolean consumerRunning = true;

public Consumer(MyTableSetting bowl, Soup s) {
    bowlView = bowl;                               // the consumer is given the GUI that will show what is happening
    soup = s;                                      // the consumer is given the soup--the monitor
}

public void run() {
    System.out.println("consuming: "+consumerRunning);
    String c;
    try {
    while(consumerRunning) {              // stop thread when know there are no more coming; here we know there will only be 10
        c = soup.eat();                            // eat it from the soup
        System.out.println("Ate a letter: " + c);  // show what happened in Console
        bowlView.repaint();                        // show it in the bowl  

            sleep((int)(Math.random() * 3000));    // have consumer sleep a little longer or sometimes we never see the alphabets!
    }    
    } catch (InterruptedException e) {
            this.interrupt();
        }
    }

}

and this:

package prodcons;

class Producer extends Thread {
private Soup soup;
private String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private MyTableSetting bowlView;
static boolean producerRunning = true;

public Producer(MyTableSetting bowl, Soup s) {
    bowlView = bowl;        // the producer is given the GUI that will show what is happening
    soup = s;               // the producer is given the soup--the monitor
}

public void run() {
    String c;
    try {
    while (producerRunning) {                           // only put in 10 things so it will stop
        System.out.println("thikns producer != null");
        c = String.valueOf(alphabet.charAt((int)(Math.random() * 26)));   // randomly pick a number to associate with an alphabet letter
        soup.add(c);                                            // add it to the soup
        System.out.println("Added " + c + " to the soup.");     // show what happened in Console
        bowlView.repaint();                                     // show it in the bowl  

            sleep((int)(Math.random() * 2000));  // sleep for a while so it is not too fast to see
    }
        } catch (InterruptedException e) {
            this.interrupt();
        }

}
}

and here is the "Soup" Class:

package prodcons;

import java.util.*;

public class Soup {
private static ArrayList <String> buffer = new ArrayList<String>();  // buffer holds what is in the soup
private int capacity = 6; 

public synchronized String eat() {      //this method can only be accessed by one thing at a time
     while(buffer.isEmpty()){                                              // cannot eat if nothing is there, so check to see if it is empty                                                 
            try {
                wait();                                                       // if so, we WAIT until someone puts something there
            } catch (InterruptedException e) {}                               // doing so temporarily allows other synchronized methods to run (specifically - add)
        }                                                                     // we will not get out of this while until something is there to eat
        String toReturn = buffer.get((int)(Math.random() * buffer.size()));   // get a random alphabet in the soup     
        buffer.remove(toReturn);                                              // remove it so no one else can eat it
        buffer.trimToSize();                                                  // reduce the size of the buffer to fit how many pieces are there
        notifyAll();                                                          // tell anyone WAITing that we have eaten something and are done
        return(toReturn);
}

public synchronized void add(String c) {
    while (buffer.size() == capacity) {
        try {
            wait();
        }
        catch (InterruptedException e) {}
    }
    buffer.add(c);
    notifyAll();
}

public ArrayList <String> getContents() {
    return buffer;
}

public static void clearBuffer() {
    buffer.clear();
}

}

Thank you so much!

TryingToCode
  • 37
  • 1
  • 4
  • 1
    don't know about the buttons, but there is a place where you call `p1.run()` and `c1.run()` from the main thread. That will _NOT_ start the threads separately, that will just execute the run method sequentially. – guido May 15 '15 at 01:29
  • show us the Soup class please; by the way, the buttons become unresponsive _after_ you click on any of them the first time, or even before that? – guido May 15 '15 at 01:42
  • I added the soup class. thanks! – TryingToCode May 15 '15 at 02:34
  • 1
    1) Why code an applet? If due to the teacher specifying it, please refer them to [Why CS teachers should **stop** teaching Java applets](http://programmers.blogoverflow.com/2013/05/why-cs-teachers-should-stop-teaching-java-applets/). 2) Why use AWT? See [this answer](http://stackoverflow.com/questions/6255106/java-gui-listeners-without-awt/6255978#6255978) for good reasons to abandon AWT using components in favor of Swing. 3) See [Detection/fix for the hanging close bracket of a code block](http://meta.stackexchange.com/q/251795/155831) for a problem I could no longer be bothered fixing. – Andrew Thompson May 15 '15 at 04:24

1 Answers1

0

You call the Producer and Consumer directly on the main UI thread from the event handler for the start button. The only effect that this will have is to freeze your entire application. That's because the main UI thread does everything: event handling, repainting, etc. As long as you hold on to it, there is going to be no event handling, repainting, etc.

You should never call anything that takes long on the main UI thread.

But since you've already started the threads in your init method, you don't need to call run in your action listener. You remove those calls:

Button start = new Button("Start");
    start.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            Producer.producerRunning = true;
            Consumer.consumerRunning = true;
        }
    });

Also, you should make the flags volatile, otherwise the change to them from one thread may not be visible in the other thread:

volatile static boolean producerRunning = true;

(etc.)

Erwin Bolwidt
  • 30,799
  • 15
  • 56
  • 79
  • Hmm you're right. Although it would make much more sense to start them when you press the "Start" button. I left that out to focus on the immediate problem. – Erwin Bolwidt May 15 '15 at 01:41
  • Thanks Erwin. I followed your suggestion with "volatile" and I took out the run() methods. Now, the thread stops with the stop button (good) but does not start with the start button. Is the UI properly communicating with the threads' while loops? – TryingToCode May 15 '15 at 02:33