CS136, Lecture 19

Concurrency

Monitors and synchronized methods

Programming with threads can be more complicated if both need to access the same data. In particular, problems can arise if two different threads are trying to modify the same variable at the same time.

To get around this, you can set up what is often called a monitor: this is a data structure that only allows access by one thread at a time.

You can declare methods in a class to be synchronized. Synchronized methods are restricted so that only one of the synchronized methods of a class can be active at a time. (Ordinarily, several threads may be executing methods of the object at the same time!)

When a synchronized method is running, a "lock" is established on the synchronized methods, so that none can be executed until the lock is released by the executing thread.

Use pair of methods wait() and notifyAll() when must wait on a condition. Wait releases lock and places thread on a queue waiting to be notified that condition might have changed. NotifyAll wakes all waiting threads and allows them to proceed (or at least determine if they want to proceed). Can also just use notify() to inform one waiting thread (but better to use notifyAll() unless know exactly what is waiting).

Wait, notifyAll, and notify can only be invoked from within synchronized methods (or methods called from within synchronized methods).

See code for circular buffer in Buffer folder. In the program provided there are threads that respectively "produce" and "consume" integers from a buffer of size 5. When a thread tries to consume an integer when none are available, it must wait. Similarly if a thread tries to produce an integer and there is no room in the buffer, the producer must wait. In each case, when an integer has been added or removed from the buffer, the buffer sends a notifyAll() to wake up all of those waiting to use the buffer. Notice that the waits are included in while loops as by the time they have been awakened, the buffer space may be gone.

Testing with JUnit

JUnit is a modern testing tool designed by Eric Gamma and Kent Beck (promoter of "Extreme Programming") that is available from www.junit.org. JUnit is a tool that reflects the philosophy of test-driven development. Test-driven development involves writing "unit" tests first, and only then writing the actual code. Unit testing is easier and more effective (at least in the early stages of programming) than writing tests of the entire project. The goal is that you never integrate a class into a project until you are convinced that it works properly. Moreover, because we often change classes, we keep the unit test so that we can rerun them after any changes are made. While anyone can do such testing, JUnit is a tool that helps you set up the tests and then runs them all automatically for you -- a huge advantage!

We will only provide a brief overview here. You can learn more on your own as well as in our advanced software development or software engineering courses. JUnit is integrated into Eclipse, so it is especially easy to use it when using Eclipse. Please read the tutorial that is available from the course web page for details as you will be using it in lab this week.

To create a test case:

  1. Create a class extending junit.framework.TestCase. The best way to do this is go select New and then other from the file menu.
    1. If necessary, click on the triangle next to Java on the left of the screen so that you can select JUnit. In the right panel, select TestCase and then click on the Next> button.
    2. Type in the name of your test class in the name field. If the class you will be testing already exists, enter it in the Class to Test field (you can also use the browse button to find it if you'd like). Click on the Next> button.
    3. You will get a window showing the methods of the class you will be testing (if you selected one). Click in the checkboxes to have method headers automatically generated for each selected method. Click on Finish and your class will be displayed.
  2. Define tests that return void and whose names begins with test. The tests should use methods like assertEquals(a,b) and assertTrue(msg,cond). If you need to set up values in advance, override the method public void setUp(). You may have as many tests as you like.

To run a test case:

  1. Select the test class in the Package Explorer pane on the left side of the Eclipse window.
  2. Pull down the menu to the right of the running figure and select run as ... and then select JUnit test.

A JUnit panel should replace the PackageExplorer on the leftmost panel in the window. If the bar across the top is green, then all tests have failed. If one or more tests have failed then the bar will be red and each failed test will be listed. Select each to find out why the test failed.

The JUnit panel on the left side of the screen can be replaced by the usual Package Explorer panel if you just click on that tab on the bottom of the screen.

Continue to refine and add to your tests (just add a method whose name starts with test and code until you are convinced that all of the methods in your class work correctly. At that point you are ready to integrate your class with the other classes in your project and test them. Again, you can write test suites for these using JUnit.

The recommended way of working with JUnit is always to write your tests before writing the code that uses them. Thus when you start, all of your tests should fail. Your goal is to fix the failed tests one at a time until you get a green bar. If you had a good test suite then your class should be correct. (Though if you had a lousy test suit, you may still have errors!)

At the beginning of Wednesday morning in class, please bring in a test class for the State class. I have given you a partially written test class. You should include the bodies of methods that test multiplication, subtraction, division, and the clear method.

import junit.framework.TestCase; 
import javax.swing.*; 
/* 
 * Created on Apr 4, 2004 
 * Test class for State class
 */ 
public class StateTest extends TestCase { 
	private JLabel display;  // dummy display for testing
	private State st;        // sample state object
	 
    // set up state with 2 on top of stack and 9 below it.
	public void setUp(){ 
		display = new JLabel("0"); 
		st = new State(display); 
		st.addDigit(9); 
		st.enter(); 
		st.addDigit(2); 
		st.enter(); 
	} 
 
    // check that if 2 on stack and add digits 7, 5, and then
    // hit enter that display will show "75"
	public void testAddDigit() { 
		assertEquals("2",display.getText()); 
		st.addDigit(7); 
		assertEquals("7",display.getText()); 
		st.addDigit(5); 
		assertEquals("75",display.getText()); 
		st.enter(); 
		assertEquals("75",display.getText()); 
	} 
 
    // check that if stack has 4, 2, and 9, then executing plus twice
    // gives "6" and then "15".
	public void testPlus() { 
		st.addDigit(4); 
		st.enter(); 
		st.doOp('+'); 
		assertEquals("6",display.getText()); 
		st.doOp('+'); 
		assertEquals("15",display.getText()); 
	} 
	 
	public void testMult() { 
	} 
	 
 
	public void testMinus() { 
	} 
	 
	public void testDiv() { 
	} 
	 
	public void testClear() { 
	} 
 
    // check if 2 and 9 on stack that if pop once get "9" in display
    // and if pop again have "0" on stack and remains if pop once more.
	public void testPop() { 
		st.pop(); 
		assertEquals("9",display.getText()); 
		st.pop(); 
		assertEquals("0",display.getText()); 
		st.pop(); 
		assertEquals("0",display.getText()); 
	} 
	 
}

Ordered Structures

Earlier in the term we talked about sorting arrays. The various sort routines had an interface:

import structure.Comparable;

public interface SortInterface
{
    /**  
    POST -- The elements are in non-decreasing order
    **/
    public void sort (Comparable[] elts);
}

where

public interface Comparable {
    public boolean compareTo(Comparable item);
    // pre: item is non-null
    // post: returns true iff this object less than item
}

In order to test the sorts, we created a class of elements which met that specification. Here we create a special class called an ordered association (Association was defined in chapter 1 of Bailey).

public class Association {
    protected Object theKey;   // key of the key-value pair
    protected Object theValue; // value of the key-value pair

    public Association(Object key, Object value)
    // pre: key is non-null.
    // post: constructs a key-value pair.
    {
		Assert.pre(key != null, "Key must not be null.");
		theKey = key;
		theValue = value;
    }

    public boolean equals(Object other)
    // pre: other is non-null Association
    // post: returns true iff the keys are equal
    {
		Association otherAssoc = (Association)other;
		return key().equals(otherAssoc.key());
    }
       
    public Object value()
    // post: returns value from association
    {
			return theValue;
    }

    public Object key()
    // post: returns key from association
    {
			return theKey;
    }

    public void setValue(Object value)
    // post: sets association's value to value.
    {
			theValue = value;
    }

    public String toString()
		...
}

Notice the type-cast that was needed in equals, because equals in Object takes a parameter of type Object, overridden method must take a parameter of the same type.

ComparableAssociation extends Association:

public class ComparableAssociation extends Association implements Comparable {
    public ComparableAssociation(Comparable key, Object value)
    // pre: key is non-null
    // post: constructs association of key and a value {
		super(key,value);
    }

	 // pre: key is non-null
	 // post: constructs association of key and null
     public ComparableAssociation(Comparable key) {
		this(key,null);
    }

	// pre: other is non-null ComparableAssociation
	// post: returns int representing relation between keys
    public int compareTo(Object other) {
       ComparableAssociation otherCA = (ComparableAssociation)other;
       Comparable thisKey = (Comparable)getKey();
       Comparable otherKey = (Comparable)otherCA.getKey();

       return thisKey.compareTo(otherKey);
    }

    public String toString()
		...
}

Note again how compareTo must perform a type cast on its parameter. Again the parameter must be of type Comparable since compareTo in Comparable does. (Isn't this annoying - a big part of my research in OO language design has been in eliminating this annoyance!).

Please read the rest of this chapter on your own. It discusses ordered vectors and ordered lists. You should understand the details of the implementations (see the code in the structures library as well as the material in the text).

Comparators

The library java.util includes the interface
public interface Comparator {
     // returns negative if o1 < 02, 0 if 01 ==02, positive if 01 > o2
     // in whatever ordering is being supported by object.
     int compare(Object o1, Object 02);
}
In Java 1.5, you would instead write:
public interface Comparator {
     int compare(T o1, T o2);
}

If you want a "weird" ordering, then define Comparator class to provide the ordering, and then pass it into your favorite sort or search method. Text provides CaselessComparator to compare strings by converting them to lower case before comparing.

Here is a Comparator that compares strings by first removing any leading or trailing whitespace (blanks, tabs, carriage returns, etc.).

public class TrimComparator implements Comparator {
   // no constructor needed, so omitted
   
   // pre: o1 and o2 are strings
   // post: returns negative, zero, or positive depending on relation 
   //       between trimmed parameters.
   public int compare(Object o1, Object o2) {
       return compare ((String)o1, (String)o2);
   }

   public int compare(String s1, String s2) {
       String s1trim = s1.trim();
       String s2trim = s2.trim();
       return s1trim.compareTo(s2Trim);
   }
}
In Java 1.5 we would just write:
public class TrimComparator implements Comparator

Use comparator in a sort as follows: public static void quickSort(Object[] data, Comparator c) { ... // replace all occurrences of a.compareTo(b) with c.compare(a,b) ... }

Suppose have comparator-based sort and just want to use regular ordering on that data type. Easy to accomodate if we write (just once!) the following class:
public class NaturalComparator implements Comparator {
   // pre: o1 is an instance of Comparable
   // post:  as usual
   public int compare(Object o1, Object o2) {
      return ((Comparable)o1).compareTo(o2);
   }
}