Advanced Programming: Concurrency, Lab 5

(apologies for the page's design - one should never use Word to edit HTML pages...)

In this lab you will learn how to create a simple GUI (as an Applet or a normal Java GUI app), and threads in Java. This lab uses the example "Ornamental Garden Problem" from the book Concurrency: State Models & Java Programs. (https://www.doc.ic.ac.uk/~jnm/book/book_applets/Garden.html).

 

Create an applet

Import the packages Applet and Graphics and create class Garden that extends Applet.

package concurrency.garden;
import java.applet.*;
import java.awt.*;
 
 
public class Garden extends Applet {
}

Import the packages swing and Graphics and create class Garden that extends Component.

package concurrency.garden;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
 
public class Garden extends Component {
}

Create the GUI
Create the interface of the applet shown in (https://www.doc.ic.ac.uk/~jnm/book/book_applets/Garden.html). The GUI contains one button, one checkbox and three canvas components. To use the Canvas class in order to get the functionality we need to extend it. At least the paint method must be overridden in order to perform custom graphics on the canvas. To see more details about the Canvas class look at its java documentation (https://docs.oracle.com/javase/8/docs/api/java/awt/Canvas.html).

Create a new class NumberCanvas that extends Canvas as the following code suggests:

package concurrency.display;
import java.awt.*;
import java.applet.*;
public class NumberCanvas extends Canvas {
 
}
package concurrency.display;
import java.awt.*;
 
public class NumberCanvas extends Canvas {
 
}

Our NumberCanvas class must include one title at the top, as well as the value of the number of people that have entered in the Garden. Therefore you have to add fields value_ and title_ and a setvalue method to update the value, besides from the constructor for the NumberCanvas class. Add the code below to the NumberCanvas class:

...
public class NumberCanvas extends Canvas {
 
  int value_ = 0;
  String title_ = null;
 
  public NumberCanvas (String title) {
    this(title,Color.cyan); // re-uses the next constructor!
  }
 
  public NumberCanvas (String title, Color c) {
    super();
    title_=title;
    setBackground(c);
  }
 
  // The repaint method calls the method paint that updates the GUI.
  // We will explain the paint method later.
  public void setvalue(int newval) {
    value_ = newval;
    repaint();
 
  }
}

Now you have to override method paint(Graphics g) in class NumberCanvas in order to show the title_ and value_ fields. To draw strings and lines we can use the methods drawString and drawLine respectively.
The code uses objects f1 and f2 for drawing the title and value using diferent fonts. The FontMetrics class is used to get information about the size of the font type in order to adjust the presentation of the title and the value. Add the following code to class NumberCanvas.

 
...
public class NumberCanvas extends Canvas {
 
...
 
  Font f1 = new Font("Helvetica",Font.BOLD,36);
  Font f2 = new Font("Times",Font.ITALIC+Font.BOLD,24);
 
...
 
  public void setcolor(Color c) {
    setBackground(c);
    repaint();
  }
 
  public void paint(Graphics g){
 
    g.setColor(Color.black);
 
    // Display the title
    g.setFont(f2);
    FontMetrics fm = g.getFontMetrics();
    int w = fm.stringWidth(title_);
    int h = fm.getHeight();
    int x = (getSize().width - w)/2;
    int y = h;
    g.drawString(title_, x, y);
    g.drawLine(x,y+3,x+w,y+3);
 
    // Display the value
    g.setFont(f1);
    fm = g.getFontMetrics();
    String s1 = String.valueOf(value_);
    w = fm.stringWidth(s1);
    h = fm.getHeight();
    x = (getSize().width - w)/2;
    y = (getSize().height+ h)/2;
    g.drawString(s1, x, y);
  }
}

Now that we have finished the NumberCanvas class we can add the AWT components to the Garden class. The first thing that you have to do is declare the AWT components and import the package awt. Add the following lines in your code.

import java.awt.*;
 
...
 
public class Garden extends Applet {
 
  Button goButton;
  NumberCanvas counterD;
  NumberCanvas westD;
  NumberCanvas eastD;
  Checkbox fixit;
 
  ...
}

The class Applet contains a set of methods and fields that you can check in (https://docs.oracle.com/javase/8/docs/api/java/applet/Applet.html). There are some methods that are invoked automatically by the browser or applet viewer, such as:

init( )

initialise the applet each time it's loaded (or reloaded).

start( )

start the applet's execution, such as when the applet's loaded or when the user revisits a page that contains the applet.

stop( )

stop the applet's execution, such as when the user leaves the applet's page or quits the browser.

destroy( )

perform a final cleanup in preparation for unloading.

To see more information on how to create an Applet see the Java Tutorial (Java Applet Tutorial - the old tutorial is here).

So, we need to initialize the GUI components declared inside the init method. Override the init method as shown below:

 
...
public class Garden extends Applet {
  ...
 
  public void init() {
    super.init();
    // Set up Button and Checkbox
    Panel p0= new Panel();
    p0.add(goButton = new Button(" Go "));
    goButton.setFont(new Font("Helvetica",Font.BOLD,24));
    // [go] event handler  - inside Garden()!!!
    Panel p1= new Panel();
    p1.setLayout(new BorderLayout());
    p1.add("Center",p0);
    p1.add("East",fixit = new Checkbox("Fix It"));
    // Set up Display
    Panel p2 = new Panel();
    counterD = new NumberCanvas("Counter");
    westD   = new NumberCanvas("West",Color.green);
    eastD   = new NumberCanvas("East",Color.green);
    counterD.setSize(150,100);
    westD.setSize(100,100);
    eastD.setSize(100,100);
    p2.add(westD);
    p2.add(counterD);
    p2.add(eastD);
    // Arrange Applet display
    setLayout(new BorderLayout());
    add("Center",p2);
    add("South",p1);

  }       
  ...
}
import java.awt.*;
 
...
 
public class Garden extends Component {
 
  Button goButton;
  NumberCanvas counterD;
  NumberCanvas westD;
  NumberCanvas eastD;
  Checkbox fixit;
  JFrame frame;
  ...
}

The class needs a main function.

  static public void main(String[] args) {
     Garden me = new Garden();
  }

 

 

 

 

 

 

 

So, we need to initialize the GUI components declared inside the constructor:

 
...
public class Garden extends Component {
  ...
 
  public Garden() {
    frame= new JFrame(); // To draw upon
// Set up Button and Checkbox
    Panel p0= new Panel();
    p0.add(goButton = new Button(" Go "));
    goButton.setFont(new Font("Helvetica",Font.BOLD,24));
    // [go] event handler  - inside Garden()!!!
    Panel p1= new Panel();
    p1.setLayout(new BorderLayout());
    p1.add("Center",p0);
    p1.add("East",fixit = new Checkbox("Fix It"));
    // Set up Display
    Panel p2 = new Panel();
    counterD = new NumberCanvas("Counter");
    westD   = new NumberCanvas("West",Color.green);
    eastD   = new NumberCanvas("East",Color.green);
    counterD.setSize(150,100);
    westD.setSize(100,100);
    eastD.setSize(100,100);
    p2.add(westD);
    p2.add(counterD);
    p2.add(eastD);
    // Arrange Applet display
    frame.setLayout(new BorderLayout());        // new
    frame.add("Center",p2);     // new
    frame.add("South",p1);      // new
    frame.pack();
    frame.setVisible(true);
// [go] event handler
  }
  ...
}

Testing the Garden applet

Testing the Garden application

To run an applet, you first need to add the applet to an HTML page, using the <APPLET> tag.

1.     Compile files NumberCanvas.java and Garden.java to get the .class files needed:

 
javac concurrency/garden/Garden.java concurrency/display/NumberCanvas.java

Note that the two files must be in these directories.
Why???
Because of the packages they have been declared to be inside - check the first lines of the respective Java files.

Where's javac? (echo $PATH vs %PATH%)

2.     Create a new file named garden.html. Save garden.html at the top directory of your package hierarchy. In this case, it should be at the directory which contains the "concurrency" directory.

3.     Enter the following code in the garden.html file:

 
<HTML>
<BODY>
<APPLET CODEBASE="." CODE="concurrency.garden.Garden" WIDTH="400" HEIGHT="150" >
</APPLET>
</BODY>
</HTML>

4.     Now open file garden.html in your IDE (Eclipse, Netbeans) OR use appletviewer on the command line:

 
appletviewer -J"-Djava.security.policy=java.policy.applet" garden.html

The -J... option is needed to override the default security policy of the JVM, since Java applets are currently considered a security risk (we're only using them for teaching purposes, not to develop a real application!). The policy file needed is linked in the command above - store it locally where your garden.html file is.

Where's appletviewer? See where's javac above.

javac concurrency/garden/Garden.java concurrency/display/NumberCanvas.java 



java concurrency/garden/Garden
 

 

 

Creating Threads

We need to create two threads that represent the Turnstiles. To create a thread in Java you have two techniques: extend Thread and override the run method or implement the Runnable interface (see the example ThreadDemo of the Book "Concurrency: State Models & Java Programs"- https://www.doc.ic.ac.uk/~jnm/book/book_applets/ThreadDemo.html)

If you need to extend another class, you *must* implement the Runnable interface since Java doesn't allow multiple inheritance. In our case we are going to extend the Thread class. To see more information about threads see the Java Tutorial (https://download.oracle.com/javase/tutorial/essential/concurrency/threads.html - the old version is here https://web.archive.org/web/20080629101626rn_1/java.sun.com/docs/books/tutorial/essential/concurrency/). (note the books referenced in the "For Further Reading" section of the Java SE Tutorial :-) )

To add behaviour to the thread you need to override its run method. In our example we need to create a class Turnstile that overrides the run method to increment a counter 20 times. To do that, enter the code below:

class Turnstile extends Thread {
  NumberCanvas display;
  Counter people;
 
  Turnstile(NumberCanvas n,Counter c)
    { display = n; people = c; }
 
  public void run() {
    try{
      display.setvalue(0);
      for (int i=1;i<=20;i++){
        Thread.sleep(500); //0.5 second
        display.setvalue(i);
        people.increment();
      }
    } catch (InterruptedException e) {}
  }
}

You might have noticed that class Turnstile has an object people of class Counter. Therefore you need to create class Counter that has a value field and can increment it. Enter the following code:

class Counter {
 
    int value=0;
    NumberCanvas display;
 
    Counter(NumberCanvas n) {
        display=n;
        display.setvalue(value);
    }
 
    void increment() {
        int temp = value;   //read[v]
        Simulate.HWinterrupt();
        value=temp+1;       //write[v+1]
        display.setvalue(value);
    }
}

To observe the phenomenon of interference, the method HWinterrupt was added that sometimes (randomly) switches the thread currently running by calling method sleep(). Therefore, you need to create class Simulate as follows:

class Simulate {
    public static void HWinterrupt() {
        if (Math.random()<0.5)
           try{Thread.sleep(200);} catch(InterruptedException e){};
            //used instead of Thread.yield() to ensure portability
    }
}

To avoid interference you need to create a class that uses the synchronized keyword when incrementing the number of people. Create the class SynchronizedCounter:

 
class SynchronizedCounter extends Counter {
 
  SynchronizedCounter(NumberCanvas n)
     {super(n);}
 
   synchronized void increment() {
        super.increment();
   }

}

Now, you just have to add actions to button Go. To do that add the following code to the Garden class:

...
public class Garden extends ... {
  Turnstile east;
  Turnstile west;
  Counter counter;
 
  private void go() {
    if (!fixit.getState())
      counter = new Counter(counterD);
    else
      counter = new SynchronizedCounter(counterD);
    west= new Turnstile(westD,counter);
    east= new Turnstile(eastD,counter);
    west.start();
    east.start();
  }

 

 

// [go] event handler  - inside Garden()!!!
goButton.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
           if (west==null && east==null)
              go();
          else if (!west.isAlive() && !east.isAlive())
              go();    
          }   
        }); 

 

Now compile and test your application.

 

 

The files we've developed: