Multithreading • Basics – thread state: runnable, blocked Multithreading – start, sleep, interrupt • Synchronization – race condition Horstmann ch.9 – locks – deadlock avoidance: await, notifyall • Animation – example: mergesort Threads Running Threads • Define class that implements Runnable • Thread: program unit that is executed independently • Runnable has one method • Multiple threads run simultaneously void run() • Virtual machine executes each thread for short • Place thread action into run method time slice • Construct object of class • Thread scheduler activates, deactivates threads • Construct thread from that object • Illusion of threads running in parallel • Multiprocessor computers: threads actually run • Start thread in parallel Running Threads Thread Example • Run two threads in parallel public class MyRunnable implements Runnable { • Each thread prints 10 greetings public void run() { thread action } for (int i = 1; i <= 10; i++) { } System.out.println(i + ": " + greeting); Thread.sleep(100); ... } Runnable r = new MyRunnable(); • After each printout, sleep for 100 millisec Thread t = new Thread(r); • All threads should occasionally yield control t.start(); • sleep throws InterruptedException
Class GreetingProducer Thread Example public class GreetingProducer implements Runnable { public GreetingProducer(String aGreeting) { greeting = aGreeting; } public void run() { try { for (int i = 1; i <= REPETITIONS; i++) { System.out.println(i + ": " + greeting); Thread.sleep(DELAY); } } catch (InterruptedException exception) { } } private String greeting; private static final int REPETITIONS = 10; private static final int DELAY = 100; } Class ThreadTest Thread Example public class ThreadTest { • Note: output not exactly 7: Hello, World! interleaved 7: Goodbye, World! public static void main(String[] args) { 8: Goodbye, World! Runnable r1 = new GreetingProducer("Hello, World!"); 8: Hello, World! 1: Hello, World! Runnable r2 = new GreetingProducer("Goodbye, World!"); 9: Goodbye, World! 1: Goodbye, World! Thread t1 = new Thread (r1); 9: Hello, World! 2: Hello, World! Thread t2 = new Thread (r2); 10: Goodbye, World! 2: Goodbye, World! t1. start (); 10: Hello, World! 3: Hello, World! t2. start (); 3: Goodbye, World! } 4: Hello, World! } 4: Goodbye, World! 5: Hello, World! 5: Goodbye, World! 6: Hello, World! 6: Goodbye, World! Starting Two Threads Thread States • Each thread has – thread state – priority • Thread states: – new (before start called) – runnable – blocked – dead (after run method exits)
Thread States Blocked Thread State • Reasons for blocked state: – Sleeping – Waiting for I/O – Waiting to acquire lock (later) – Waiting for a condition (later) • Unblocks only if reason for block goes away Scheduling Threads Terminating Threads • Scheduler activates new thread if • Thread terminates when run exits – a thread has completed its time slice • Sometimes necessary to terminate – a thread has blocked itself running thread – a thread with higher priority has become runnable • Don't use deprecated stop method • Scheduler determines new thread to run • Interrupt thread by calling interrupt – looks only at runnable threads • Thread must cooperate and exit its run – picks one with max priority • Thread has chance to clean up Sensing Interruptions Sensing Interruptions • Thread could occasionally call public class MyRunnable implements Runnable { public void run() { Thread.currentThread().isInterrupted try { () while (...) { • sleep , wait throw InterruptedException do work when thread interrupted Thread.sleep(...); } • . . . and then the interruption status is cleared! } catch (InterruptedException e) {} clean up • More robust: Catch exception and react to } interruption } • Recommendation: Terminate run when sensing interruption
Thread Synchronization Producer Thread int i = 1; • Use bounded queue from chapter 8 while (i <= greetingCount) { • Each producer thread inserts greetings if (! queue .isFull()) { queue .add(i + ": " + greeting); • Each consumer thread removes greetings i++; • Two producers, one consumer } Thread.sleep((int)(Math.random() * DELAY)); } all threads use same queue Consumer Thread int i = 1; • Expected Program • Why is Output while (i <= greetingCount) { Output Corrupted? if (! queue .isEmpty()) { • Sometimes program gets Object greeting = queue .remove(); 1: Hello, World! System.out.println(greeting); stuck and doesn't 1: Goodbye, World! i++; 2: Hello, World! complete } 3: Hello, World! • Can see problem better ... Thread.sleep((int)(Math.random() * DELAY)); when turning debugging 99: Goodbye, World! } on 100: Goodbye, World! queue.setDebug(true); add method in Queue Circular Array Implementation public void add(E newValue) { if (debug) System.out.print("add"); elements[tail] = newValue; if (debug) System.out.print("."); tail++; if (debug) System.out.print("."); size++; if (tail == elements.length) { • Efficient implementation of bounded queue if (debug) System.out.print("."); tail = 0; • Avoids inefficient shifting of elements } • Circular: head, tail indexes wrap around if (debug) System.out.println("head=" + head + ",tail=" + tail + ",size=" + size); • In circular array implementation, failure of remove } precondition corrupts queue!
Race Condition remove method in Queue Scenario public E remove() { if (debug) System.out.print("removeFirst"); • First thread calls add and executes E r = (E) elements[head]; elements[tail] = newValue; if (debug) System.out.print("."); • First thread at end of time slice head++; • Second thread calls add and executes if (debug) System.out.print("."); elements[tail] = newValue; size--; tail++; if (head == elements.length) { • Second thread at end of time slice if (debug) System.out.print("."); • First thread executes head = 0; tail++; } if (debug) System.out.println("head=" + head + ",tail=" + tail + ",size=" + size); return r; } Locks Reentrant Locks aLock = new ReentrantLock(); • Thread can acquire lock . . . • When another thread tries to acquire same lock, aLock.lock(); it blocks try { • When first thread releases lock, other thread is protected code unblocked and tries again } • Two kinds of locks finally { – Objects of class implementing aLock.unlock(); java.util.concurrent.Lock interface type, usually } ReentrantLock – Locks that are built into every Java object Scenario with Locks Deadlocks • Assume body of add method protected by lock • Not enough to protect bodies of add , remove • if (!queue.isFull()) queue.add(...); • First thread calls add and acquires lock, then executes can still be interrupted elements[tail] = anObject; • Must move test inside add method • Second thread calls add and tries to acquire lock, but it public void add(E newValue) { is blocked queueLock.lock(); try { while ( queue is full ) • First thread executes wait for more space tail++; . . . • First thread completes add , releases lock } finally { queueLock.unlock(); } } • Second thread unblocked • Second thread acquires lock, starts executing protected • Problem: nobody else can call remove code
Avoiding deadlocks Avoiding Deadlocks • Waiting thread is blocked • Use conditon object to manage "space available" condition • Condition object manages set of threads that wait for the condition to change private Lock queueLock = new ReentrantLock(); private Condition spaceAvailableCondition = • To unblock, another thread must call signalAll on the same condition queueLock.newCondition(); object • Call when state changes • Call await when condition is not fulfilled: public E remove() { public void add(E newValue) { . . . . . . E r = (E) elements[head]; while (size == elements.length) . . . spaceAvailableCondition.await(); spaceAvailableCondition.signalAll(); . . . // Unblock waiting threads } return r; } • All waiting threads removed from wait set, unblocked Object Locks Object Locks • Each object has a lock • Each implicit lock has one associated (anonymous) condition object • Calling a synchronized method acquires lock of implicit parameter • Object.wait blocks current thread and adds it to wait set • Leaving the synchronized method releases lock • Object.notifyAll unblocks waiting threads • Easier than explicit Lock objects public synchronized void add(E newValue public class BoundedQueue<E> { throws InterruptedException { public synchronized void add(E newValue) { ... } while (size == elements.length) wait(); elements[tail] = anObject; public synchronized E remove() { ... } . . . notifyAll(); ... // notifies threads waiting to remove elements } } Visualizing Locks Algorithm Animation • Use thread to make progress in algorithm • Object = phone booth • Display algorithm state • Thread = person • Example: Animate MergeSorter (similar to java.util.Arrays.sort ) • Pause inside compare method • Locked object = closed booth • Pass custom comparator • Blocked thread = person waiting for booth to open Comparator<Double> comp = new Comparator<Double>() { public int compare(Double d1, Double d2) { draw current state pause thread return d1.compareTo(d2); } };
Recommend
More recommend