These notes are directly from the slides. I have not taken these notes personally.

Week 7

Thread safety

  • Safety properties are conditions that should hold throughout the lifetime of a program.
    • stipulate that nothing bad should ever happen
    • interrupted threads may leave an object in an invalid state
public class Maze {
	private int playerX;
	private int playerY;
 
	public boolean isAtExit() {
		return (playerX == 0 && playerY == 0);
	}
	public void setPosition(int x, int y) {
		playerX = x;
		playerY = y;
	}
}

Controlling Threads

  • state of a thread
    • new
    • alive
      • runnable
      • blocked
    • dead
  • priorities
    • always runs highest priority thread
      • random choice, among those with same priority
      • preemptive, e.g., a thread of higher priority will preempt a thread with lower priority
    • use priorities only to tune the performance of programs

Synchronization

This code is thread-safe:

public class Maze {
	private int playerX;
	private int playerY;
 
	public synchronized boolean isAtExit() {
		return (playerX == 0 && playerY == 0);
	}
	public synchronized void setPosition(int x, int y) {
		playerX = x;
		playerY = y;
	}
}

When the JVM executes a synchronized method, it acquires a lock on that object.

  • if one synchronized method owns a lock, no other synchronized method can run until the lock is released
  • only one lock on an object at a time
  • lock is released when the method is finished

Do not oversynchronize!!

  • synchronize if one or more threads will access the same object or field.
  • do not synchronize an entire method if only parts of the method need to be synchronized
public void myMethod() {
		synchronize(this) {
			// code that needs to be synchronized
		}
		// code that is already thread-safe
	}
  • do not synchronize a method that uses only local variables:
//a method which should not be synchronized
public int square(int n) {
	int s = n * n;
	return s;
} 

Using sleep()

Thread.sleep(1000);

  • causes the currently running thread to sleep for 1000 (or any amount of time given as an argument) miliseconds (state blocked)
  • a sleeping thread does not consume any CPU time
  • when the specified duration of time expires the thread returns to the runnable state

Using wait() and notify()

Problem:   Thread A should wait on Thread B to send a message:

Solution 1:

//Thread A
public void waitForMessage() {
	while (!hasMessage) {
		Thread.sleep(100);
	}
}
 
//Thread B
public void sendMessage(String message) {

	hasMessage = true;
}

Solution 2:

//Thread A
public synchronized void waitForMessage() {
   if (!hasMessage) { 
	try {
		wait();
	}
	catch (InterruptedException ex) {}
}
}
 
//Thread B
public synchronized void sendMessage(String message) {

	hasMessage = true;
	notify();
}

The wait(), notify() and notifyAll() methods are defined in the class Object.

  • the wait() method is used in synchronized blocks of code.
    • the lock is released and the thread waits to be notified (state blocked)
  • the notify() method is also used in synchronized blocks of code.
    • notifies on thread waiting on the same lock (randomly)
    • waiting thread becomes runnable
  • variants
    • wait for a maximum amount of time: wait(100);
      • there is no way to tell whether the wait() method returned because of a timeout or because the thread was notified
    • notify all threads waiting on the lock: notifyAll();

Using join()

The join() method causes a thread to enter the blocked state and wait for another thread to finish, at which time it will be returned to the runnable state.

  • useful, when you want to make sure all threads are finished before you do some cleanup
public static void main(String[] args) {
 
	Thread playerA = new Thread() {
	 	public void run() {
	 		System.out.println("A started");
	 		try { Thread.sleep(10000); }
	 		catch (InterruptedException e) {};
	 		System.out.println("A terminated");
		}
	 };
	 Thread playerB = new Thread() {
	 	public void run() {
	 		System.out.println("B started");
	 		try { Thread.sleep(15000); }
	 		catch (InterruptedException e) {};
	 		System.out.println("B terminated");
	 	}
	 };
	 playerA.start();
	 playerB.start();
	 try {
	 	playerA.join();
	 	playerB.join();
	 }
	 catch (InterruptedException e) {};
	 System.out.println("Cleanup");
}

Deadlock

Deadlock is the result of two threads that stall because they are waiting on each other to do something. General situation:

  1. Thread A acquires lock 1.
  2. Thread B acquires lock 2.
  3. Thread B waits for lock 1 to be released.
  4. Thread A waits for lock 2 to be released.

Liveness

Liveness properties stipulate that something positive will eventually happen. Examples:

  1. A certain task will be completed eventually.
  2. A thread should always respond to user input.
  3. The status of certain systems must be displayed and updated constantly.

Common types of liveness failures:

  • deadlock
  • contention
    • aka starvation or indefinite postponement
    • thread never gets a chance to run
    • sleep() or yield()
  • dormancy
    • blocked thread never released
    • failure to call notify()
  • premature termination

Example - Thread pool

A thread pool is a group of threads designed to execute arbitrary tasks.

  • limits the number of threads on the system for processor-intensive tasks
ThreadPool threadPool = new ThreadPool(3);
for (int i=0; i < 8; i++) { 
	threadPool.runTask(createTask(i));
}
threadPool.join();
private static final Runnable createTask() {
	return new Runnable() {
		public void run() {
		  System.out.println("Task " + taskID + ": start");
         
		  // simulate a long-running task
         try {
             Thread.sleep(500);
         }
         catch (InterruptedException ex) { };
         
		  System.out.println("Task " + taskID + ": end");
       }
};

Thread group

ThreadPool uses the ThreadGroup class. A ThreadGroup is a group of threads and some methods to modify the threads.

  • setDaemon() - changes the daemon status of this thread group. A daemon thread group is automatically destroyed when its last thread is stopped.
  • interrupt() – interrupts all threads in this thread group.
  • activeCount() - returns an estimate of the number of active threads in this thread group.
  • enumerate() - copies into the specified array (argument) every active thread in this thread group.
public class ThreadPool extends ThreadGroup {
 
    private boolean isAlive;
    private LinkedList<Runnable> taskQueue;     //Java 1.5
    private static int threadID;
    private static int threadPoolID;
 
    public ThreadPool(int numThreads) {
        super("ThreadPool-" + (threadPoolID++));
        setDaemon(true);
        isAlive = true;
        taskQueue = new LinkedList<Runnable>(); //Java 1.5
        for (int i=0; i<numThreads; i++) {
            new PooledThread().start();
        }
    }
private class PooledThread extends Thread {
	public PooledThread() {
   	super(ThreadPool.this,"PooledThread-" + (threadID++));
   }
   public void run() {
       	while (!isInterrupted()) {
        	Runnable task = null;   // get a task to run
               try {
                   task = getTask();
               }
               catch (InterruptedException ex) { }
			if (task == null) {	 //if getTask() returned null 		    return;		 //or was interrupted close this 		} 			 //thread by returning.
               try {			 //run the task, and eat any
                   task.run();	 //exceptions it throws
               }
               catch (Throwable t) {
                   uncaughtException(this, t);
               }
        }
	}
}
public synchronized void runTask(Runnable task) {
        if (!isAlive) {
            throw new IllegalStateException();
        }
        if (task != null) {
            taskQueue.add(task);
            notify();
        }
}
 
protected synchronized Runnable getTask() throws InterruptedException
    {
        while (taskQueue.size() == 0) {
            if (!isAlive) {
                return null;
            }
            wait();
        }
        return taskQueue.removeFirst();		//Java 1.5
}
public void join() {
     // notify all waiting threads that this ThreadPool is no
     // longer alive
     synchronized (this) {
         isAlive = false;
         notifyAll();
     }
     // wait for all threads to finish
     Thread[] threads = new Thread[activeCount()];
     int count = enumerate(threads);
     for (int i=0; i<count; i++) {
         try {
             threads[i].join();
         }
         catch (InterruptedException ex) { }
     }
}
 
public synchronized void close() {
       if (isAlive) {
           isAlive = false;
           taskQueue.clear();
           interrupt();
       }
}

Design Pattern Guarded Suspension

  • a concurrency pattern
  • intent:
    • avoid the deadlock situation that can occur whe na thread is about to execute an object’s synchronized method and state of the object prevents the method from executing to completion
  • motivation:
    • a Queue class in a multiple thread environment
    • call of pull when the Queue is empty (caller has to wait until an element is pushed to the queue)
  • solution:
    • if the precondition is not fulfilled wait but release the lock (in Java use wait())
public class Queue<E> {
	private ArrayList<E> data = new ArrayList<E>();

	synchronized public void push(E element) {
		data.add(element);
		notify();
	}
 
	synchronized public E pull() {
		while (data.size() == 0) {
			try { wait(); } catch (Exception e) {};
		};
		E element = data.removeFirst();
		return element;
	}
}

Design Pattern: Producer-Consumer

  • aka Pipe (just one producer)
  • a concurrency pattern
  • intent:
    • coordination of asynchronous production and consumption of information or objects
public class Producer implements Runnable {
	private Queue myQueue;

	public Producer(Queue myQueue) {
		this.myQueue = myQueue;

	}
 
	public void run() {

		myQueue.push(myData);

	}
}
public class Consumer implements Runnable {
	private Queue myQueue;

	public Consumer(Queue myQueue) {
		this.myQueue = myQueue;

	}
 
	public void run() {

		myData = myQueue.pull();

	}
}

GUI Programming

GUI Programming

  • graphical user interface
    • human-computer interface
    • replaces command-line interface
    • interface builders
  • Java AWT/Swing
    • behavior is platform independent
  • event-driven programming
    • user in control
    • user actions events
    • program responds to events

AWT and Swing

  • AWT components are heavyweight
    • associated with native components created by the underlying window system
    • appearance determined by the window system
    • different platforms = different look
  • Swing components are lightweight
    • created by the JVM
    • appearance determined by the JVM
    • different platforms = same look
  • Composite design pattern

skipping all individual components

Reaction Model

  • reactive
    • react to event as soon as it happens
    • usually for buttons and menu selections
    • write Listener and handle event
    • used when view must change immediately
      • completed data entry
      • doing input validation
      • reactive selections
  • passive
    • don’t react to event when it happens
    • wait until significant event (e.g. button press)
    • access widget to get its state/contents
    • usually for information entry
      • text boxes
      • selections