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
- always runs highest priority thread
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 lock is released and the thread waits to be notified (state
- 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
- there is no way to tell whether the
- notify all threads waiting on the lock:
notifyAll();
- wait for a maximum amount of time:
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:
- Thread A acquires lock 1.
- Thread B acquires lock 2.
- Thread B waits for lock 1 to be released.
- Thread A waits for lock 2 to be released.
Liveness
Liveness properties stipulate that something positive will eventually happen. Examples:
- A certain task will be completed eventually.
- A thread should always respond to user input.
- 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()
oryield()
- 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 theQueue
is empty (caller has to wait until an element is pushed to the queue)
- a
- solution:
- if the precondition is not fulfilled wait but release the lock (in Java use
wait()
)
- if the precondition is not fulfilled wait but release the lock (in Java use
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