Multi Threading Programming ObjectOriented Technology and Development Thread

Multi. Threading Programming Object-Oriented Technology and Development

Thread Objectives • Understand basic concurrency terminology • Create active Java objects using the Runnable interface and Thread class • Write Java objects that are thread safe – Synchronized methods and object locking • Understand concurrency concepts including thread groups, liveness vs. safety, and nondeterminism • Know tradeoffs between JVM thread implementations • Understand periodic and aperiodic tasks and how to use the Java Timer to perform scheduling

Concurrency Terminology – Simple • Process executes until it surrenders CPU • Blocking operations (I/O) may force surrender – Time-slicing • Process executes for period of time, then surrenders CPU – Priority • Processes have priorities, higher executes before lower – Preemption • Process with higher priority will interrupt lower – Round-robin • A process will not execute until all others have executed • Commonly used with priorities

Threads • Operating systems provide concurrent processes – Each process contains its own data and stack • Threads are processes within a single OS process – Sometimes called "lightweight processes" • Threads share the data of the parent processes – Object created by one thread is visible to another – Threads have their own stack for local variables

Thread Example class Proc extends Thread { protected int id; public Proc (int assigned. ID) { id = assigned. ID; } public void run() { } } } for (int loop=0; loop < 5; loop++) System. out. println("Hello from " + id + " loop=" + loop); class Main { public static void main(String args[]) { int times= (new Integer(args[0])). int. Value(); } } for (int loop=0; loop < times; loop++) { Thread p = new Proc(loop); p. start(); }

Thread Example Output Hello from 0 Hello from 1 Hello from 2 Hello from 0 Hello from 1 Hello from 2 Hello from 0 Hello from 2 Hello from 1 loop=0 loop=1 loop=2 loop=3 loop=4 Threads may execute in any order

Class Thread: An Overview of the Thread Methods • Class Thread constructors public Thread( String thread. Name ) public Thread() • • • Code for thread in thread’s run method Method sleep makes thread inactive Method interrupts a running thread Method is. Alive checks status of a thread Method set. Name sets a thread’s name Method join – Waits for thread to finish and continues from current thread

Threaded States: Life Cycle of a Thread • Thread states – Born state • Thread was just created – Ready state • Thread’s start method invoked • Thread can now execute – Running state • Thread is assigned a processor and running – Dead state • Thread has completed or exited • Eventually disposed of by system

Thread States: Life Cycle of a Thread State diagram showing the Life cycle of a thread.

Creating Java Threads • Two mechanisms for creating active Java objects 1) Derive from class Thread 2) Implement Runnable interface • Runnable defines a protocol for active objects – Contains a single method "void run()" • Thread defines a thread implementation – Thread implements Runnable

Thread Properties • Thread Methods – run() – start() – sleep(long) – yield() • Thread States – new code to execute for thread called by client to begin thread's execution suspends thread for # milliseconds forces scheduling to occur after new is called on a threaded class creates thread, but does not begin execution – runnableafter start() is called (blocked, sleep, suspend, wait) – dead completed

Thread Priorities and Thread Scheduling • Priorities defined between one and ten (high) – Five is default • Scheduling is preemptive, round-robin • Time Slicing – Not guaranteed (Solaris: no, Windows 95/NT: yes) – Threads without blocking calls (I/O) can run forever – User should call yield() to be kind to other threads • Typically, programmer not concerned with scheduling

Shared Data Access • Objects are visible to multiple threads • What if two threads access data simultaneously? – Active. Object 1 executes a method on Shared. Object – Before method completion, Active. Object 2 calls the method – Potential "race condition" • Shared. Object's state may become inconsistent : Active. Object 1 : Shared. Object : Active. Object 2

Monitors • Solution: turn a Java class into a monitor – Monitors are a synchronization mechanism defined by Hoare (Communicating Sequential Processes) • Monitor semantics – Clients must gain a lock on the monitor object before invoking methods – Clients waiting for the lock are placed in a queue – A client with a lock may surrender the lock without "leaving" the object • May later resume execution after re-obtaining the lock

Monitors in Java • Synchronized methods create monitors class Account { public synchronized deposit (Account a, int amount); public synchronized withdraw (Account a, int amount); public int balance(); } • Synchronized methods lock the object during their execution • Objects with synchronized methods have a queue of waiting threads – Static methods need only obtain the class lock – Non-static methods must lock the object and the class

Synchronization Example : Proc : Data access : Proc Creating 3 threads class Synchronization { public static void main(String args[]) { Data the. Data. Item = new Data(); for (int i=1; i <= 3; i++){ Thread p = new Proc(i, the. Data. Item); p. start(); } } }

Synchronization Example: Thread class Proc extends Thread { private int id; private Data the. Data; public Proc(int x, Data d) { id = x; the. Data = d; } public void run() { System. out. println("Thread started #" + id); try { for (int i = 0; i < 3; i++) { the. Data. access(id, i); yield(); } } catch (Exception e) {e. print. Stack. Trace(); } }

Synchronization Example: Data class Data { synchronized public void access(int who, int count) { System. out. println(this + " data object accessed by " + who + " count is " + count); } }

Example Outputs Thread started #1 Thread started #3 Data@1 cc 738 data object accessed by 1 count is 0 Thread started #2 Data@1 cc 738 data object accessed by 3 count is 0 Data@1 cc 738 data object accessed by 3 count is 1 Data@1 cc 738 data object accessed by 2 count is 0 Data@1 cc 738 data object accessed by 2 count is 1 Data@1 cc 738 data object accessed by 1 count is 2 Data@1 cc 738 data object accessed by 3 count is 2 Data@1 cc 738 data object accessed by 2 count is 2

Guarded Entries • Object may not be able to grant request – Do not want client to retry operation which is analogous to polling and too inefficient • Solution is a monitor with guarded entries protected type bank function balance; procedure deposit; //signals state change procedure withdraw (amount) [when (amount <= balance)] end bank; : Client 1 : Bank balance deposit : Client 2 withdraw

Waiting Example • Java implements guarded monitors with wait()/notify() class Bank { private float balance; synchronized void withdraw (float amount) { try { while (amount > balance) wait(); } catch(Interrupted. Exception e) { e. print. Stack. Trace(); } balance = balance - amount; } synchronized void deposit (float amount) { balance = balance + amount; notify(); } float balance() { return balance; } }

Explicit Locking • Java provides two mechanism to lock an object 1. Synchronized methods on the object's class 2. A synchronized block synchronized (<object>) { <object>. change. State(); } // obtain lock on <object> • Synchronized blocks obtain the object's lock – Responsibility is placed on the caller, not the object implementation – Potential source for errors in concurrent program

The Runnable Interface • Java's second mechanism to implement concurrency – Not all classes can extend thread (may already be a subclass) • Any class can implement Runnable – Must provide a run() method – Instances are not by default concurrent threads – Must create a Thread instance

Runnable Example class Proc implements Runnable { <same as before> } class Main { public static void main(String args[]) { int times = (new Integer(args[0])). int. Value(); for (int loop=0; loop < times; loop++) { Runnable runner = new Proc(loop); Thread proc = new Thread(runner); proc. start(); } } }

Thread Groups • Coordinate startup and shutdown of threads – Want to initialize all threads before beginning their execution – Increase performance by usingle call to affect thread state • Java thread groups – Hierarchical, groups can contain other groups – Organized by name • The default group is “main” – Have independent max/min priorities, security permissions, stop/start/suspend/resume

Thread Groups • Class Thread. Group – Create and manipulate groups of threads – Each group has unique name at creation • Parent and child thread groups – Method calls sent to parent group also sent to child groups
![Groups Example class Groups { public static void main(String args[]) throws Exception { Thread. Groups Example class Groups { public static void main(String args[]) throws Exception { Thread.](http://slidetodoc.com/presentation_image_h/09d6ba21d26bc8b62da1d8be3117c739/image-27.jpg)
Groups Example class Groups { public static void main(String args[]) throws Exception { Thread. current. Thread(). set. Priority (Thread. MAX_PRIORITY); Thread. Group a. Group = new Thread. Group("A"); a. Group. set. Max. Priority(5); Thread. Group b. Group = new Thread. Group("B"); b. Group. set. Max. Priority(5); for (int i=1; i <= 3; i++) { Thread p = new Thread(a. Group, new Proc(i)); p. start(); p = new Thread(b. Group, new Proc(i*10)); p. start(); } a. Group. suspend(); b. Group. suspend(); System. out. println("Group A"); a. Group. resume(); Thread. sleep(90); System. out. println("Group A and B"); b. Group. resume(); Thread. sleep(90); System. out. println("None"); a. Group. stop(); b. Group. stop(); } }

Groups Example class Proc implements Runnable { private int id; public Proc(int x) {id = x; } } public void run() { try { while (true) { System. out. println("Hello from " + id); Thread. yield(); } } catch (Exception e) {e. print. Stack. Trace(); } }

Example Output Group A Hello from 1 Hello from 2 Hello from 3 Hello from 1 Hello from 2 Group A and B Hello from 10 Hello from 20 Hello from 3 None Hello from 10 Hello from 2

Concurrency Limitations • Safety – Threads must synchronize access to resources • Liveness – Activities may fail to execute because of deadlock, too much synchronization, or not enough CPU cycles (starvation) • Nondeterminism – Concurrent program have many (possibly infinite) executions • Thread vs. method call efficiency – Not all messages are asynchronous – Constructing a thread to handle a message is much slower than invoking an object’s method

Liveness Prevention • Contention – Other processes and threads utilizing CPU • Dormancy – Thread fails to become runnable (wait() with no notify()) • Deadlock – Waiting for a resource which will never become available – All threads should allocate resource in same order

JVM Thread Implementations • Native vs. non-native (e. g. , “green” threads) – Native: thread resources managed (scheduled) by underlying OS – Non-native: threads managed by JVM, JVM is simply an OS process • Native advantages – Leverage multiprocessor hardware – Can use thread libraries written in other languages – VM can avoid inefficient I/O calls required by green threads

Scheduling Issues • Ensure all threads (tasks) have sufficient CPU cycles • Periodic/Aperiodic Tasks – Periodic tasks execute on a regular schedule • Take a temperature reading from a sensor every 100 ms – Aperiodic tasks execute as response to an event • User selects “spell check” • Task types – Execution: at precise date/time or after specified delay – Periodic: execute every …

Java’s Timer Classes • Timers schedule tasks (threads) for future background execution • Java timer-related classes – Timer • Schedules tasks for execution – Timer. Task • Base Class for all tasks scheduled by a Timer

Timer Example import java. util. *; class Timer. Example { public static void main(String args[]) throws Exception { Timer timer = new Timer(); timer. schedule(new My. Task(1), 4000); timer. schedule(new My. Task(2), 2000); for (int i=0; i < 5; i++) { System. out. println("tick"); Thread. sleep(1000); } //Periodic tasks timer. schedule. At. Fixed. Rate(new My. Task(3), 0, 500); timer. schedule. At. Fixed. Rate(new My. Task(4), 0, 200); } } Thread. sleep(1000); timer. cancel(); class My. Task extends Timer. Task { } int id; public My. Task(int my. Id) {id = my. Id; } public void run() {System. out. println("Executed #" + id); }

Timer Example Output » » » » tick Executed #2 tick Executed #1 tick Executed #3 Executed #4 Executed #3 Executed #4 // executed once after 2 seconds // executed once after 4 seconds

Thread Synchronization (Revisited) • Java uses monitors for thread synchronization • The sychronized keyword – Every synchronized method of an object has a monitor – One thread inside a synchronized method at a time – All other threads block until method finishes – Next highest priority thread runs when method finishes

Producer/Consumer Relationship without Synchronization • Buffer – Shared memory region • Producer thread – Calls produce method to add item to buffer – Calls wait if consumer has not read last message in buffer – Writes to empty buffer and calls notify for consumer • Consumer thread – Reads message from buffer – Calls wait if buffer empty • Synchronize threads to avoid corrupted data

1 2 3 4 5 6 7 8 9 10 11 13 14 15 16 17 18 19 20 21 22 23 24 Produce. Integer. java // Definition of threaded class Produce. Integer public class Produce. Integer extends Thread { private Hold. Integer. Unsynchronized shared. Object; // initialize Produce. Integer thread object public Produce. Integer( Hold. Integer. Unsynchronized shared ) { super( "Produce. Integer" ); shared. Object = shared; } // Produce. Integer thread loops 10 times and calls // shared. Object's set. Shared. Int method each time public void run() { for ( int count = 1; count <= 10; count++ ) { // sleep for a random interval try { Thread. sleep( ( int ) ( Math. random() * 3000 ) ); }

33 25 26 27 29 30 31 32 34 35 36 37 38 39 // process Interrupted. Exception during sleep catch( Interrupted. Exception exception ) { System. err. println( exception. to. String() ); } // call shared. Object method from this // thread of execution shared. Object. set. Shared. Int( count ); } System. err. println( get. Name() + " finished producing values" + "n. Terminating " + get. Name() ); } } // end class Produce. Integer

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 // Consume. Integer. java // Definition of threaded class Consume. Integer public class Consume. Integer extends Thread { private Hold. Integer. Unsynchronized shared. Object; // initialize Consumer. Integer thread object public Consume. Integer( Hold. Integer. Unsynchronized shared ) { super( "Consume. Integer" ); shared. Object = shared; } // Consume. Integer thread loops until it receives 10 // from shared. Object's get. Shared. Int method public void run() { int value, sum = 0; do { // sleep for a random interval try { Thread. sleep( (int) ( Math. random() * 3000 ) ); }

35 27 28 29 30 31 32 33 34 36 37 38 39 40 41 // process Interrupted. Exception during sleep catch( Interrupted. Exception exception ) { System. err. println( exception. to. String() ); } value = shared. Object. get. Shared. Int(); sum += value; } while ( value != 10 ); System. err. println( get. Name() + " retrieved values totaling: " + sum + "n. Terminating " + get. Name() ); } } // end class Consume. Integer

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // Hold. Integer. Unsynchronized. java // Definition of class Hold. Integer. Unsynchronized. public class Hold. Integer. Unsynchronized { private int shared. Int = -1; // unsynchronized method to place value in shared. Int public void set. Shared. Int( int value ) { System. err. println( Thread. current. Thread(). get. Name() + " setting shared. Int to " + value ); shared. Int = value; } // unsynchronized method return shared. Int's value public int get. Shared. Int() { System. err. println( Thread. current. Thread(). get. Name() + " retrieving shared. Int value " + shared. Int ); return shared. Int; } } // end class Hold. Integer. Unsynchronized

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // Shared. Cell. java // Show multiple threads modifying shared object. public class Shared. Cell { // execute application public static void main( String args[] ) { Hold. Integer. Unsynchronized shared. Object = new Hold. Integer. Unsynchronized(); // create threads Produce. Integer producer = new Produce. Integer( shared. Object ); Consume. Integer consumer = new Consume. Integer( shared. Object ); // start threads producer. start(); consumer. start(); } } // end class Shared. Cell

Consume. Integer retrieving shared. Int value Produce. Integer setting shared. Int to 1 Produce. Integer setting shared. Int to 2 Consume. Integer retrieving shared. Int value Produce. Integer setting shared. Int to 3 Produce. Integer setting shared. Int to 4 Produce. Integer setting shared. Int to 5 Consume. Integer retrieving shared. Int value Produce. Integer setting shared. Int to 6 Produce. Integer setting shared. Int to 7 Produce. Integer setting shared. Int to 8 Consume. Integer retrieving shared. Int value Produce. Integer setting shared. Int to 9 Consume. Integer retrieving shared. Int value Produce. Integer setting shared. Int to 10 Produce. Integer finished producing values Terminating Produce. Integer Consume. Integer retrieving shared. Int value Consume. Integer retrieved values totaling: Terminating Consume. Integer -1 -1 2 5 8 8 9 9 10 49

Producer/Consumer Relationship with Thread Synchronization • Synchronize threads to ensure correct data

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // Produce. Integer. java // Definition of threaded class Produce. Integer public class Produce. Integer extends Thread { private Hold. Integer. Synchronized shared. Object; // initialize Produce. Integer thread object public Produce. Integer( Hold. Integer. Synchronized shared ) { super( "Produce. Integer" ); shared. Object = shared; } // Produce. Integer thread loops 10 times and calls // shared. Object's set. Shared. Int method each time public void run() { for ( int count = 1; count <= 10; count++ ) { // sleep for a random interval try { Thread. sleep( ( int ) ( Math. random() * 3000 ) ); }

34 25 26 27 28 29 30 31 32 33 35 36 37 38 39 // process Interrupted. Exception during sleep catch( Interrupted. Exception exception ) { System. err. println( exception. to. String() ); } // call shared. Object method from this // thread of execution shared. Object. set. Shared. Int( count ); } System. err. println( get. Name() + " finished producing values" + "n. Terminating " + get. Name() ); } } // end class Produce. Integer

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 // Consume. Integer. java // Definition of threaded class Consume. Integer public class Consume. Integer extends Thread { private Hold. Integer. Synchronized shared. Object; // initialize Consumer. Integer thread object public Consume. Integer( Hold. Integer. Synchronized shared ) { super( "Consume. Integer" ); shared. Object = shared; } // Consume. Integer thread loops until it receives 10 // from shared. Object's get. Shared. Int method public void run() { int value, sum = 0; do { // sleep for a random interval try { Thread. sleep( ( int) ( Math. random() * 3000 ) ); }

35 27 28 29 30 31 32 33 34 36 37 + 38 39 40 41 // process Interrupted. Exception during sleep catch( Interrupted. Exception exception ) { System. err. println( exception. to. String() ); } value = shared. Object. get. Shared. Int(); sum += value; } while ( value != 10 ); System. err. println( get. Name() + " retrieved values totaling: " + sum "n. Terminating " + get. Name() ); } } // end class Consume. Integer

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 // Hold. Integer. Synchronized. java // Definition of class Hold. Integer. Synchronized that // uses thread synchronization to ensure that both // threads access shared. Int at the proper times. public class Hold. Integer. Synchronized { private int shared. Int = -1; private boolean writeable = true; // condition variable // synchronized method allows only one thread at a time to // invoke this method to set the value for a particular // Hold. Integer. Synchronized object public synchronized void set. Shared. Int( int value ) { while ( !writeable ) { // not the producer's turn // thread that called this method must wait try { wait(); } // process Interrupted exception while thread waiting catch ( Interrupted. Exception exception ) { exception. print. Stack. Trace(); } } System. err. println( Thread. current. Thread(). get. Name() + " setting shared. Int to " + value ); // set new shared. Int value shared. Int = value;

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 // indicate that producer cannot store another value until // a consumer retrieve current shared. Int value writeable = false; // tell a waiting thread to become ready notify(); } // synchronized method allows only one thread at a time to // invoke this method to get the value for a particular // Hold. Integer. Synchronized object public synchronized int get. Shared. Int() { while ( writeable ) { // not the consumer's turn // thread that called this method must wait try { wait(); } // process Interrupted exception while thread waiting catch ( Interrupted. Exception exception ) { exception. print. Stack. Trace(); } }

68 60 61 62 63 64 65 66 67 69 70 71 72 // indicate that producer cant store another value // because a consumer just retrieved shared. Int value writeable = true; // tell a waiting thread to become ready notify(); System. err. println( Thread. current. Thread(). get. Name() + " retrieving shared. Int value " + shared. Int ); return shared. Int; } } // end class Hold. Integer. Synchronized

1 2 3 4 5 6 7 8 10 11 12 14 16 17 18 19 20 21 22 // Shared. Cell. java // Show multiple threads modifying shared object. public class Shared. Cell { // execute application public static void main( String args[] ) { Hold. Integer. Synchronized shared. Object = new Hold. Integer. Synchronized(); // create threads Produce. Integer producer = new Produce. Integer( shared. Object ); Consume. Integer consumer = new Consume. Integer( shared. Object ); // start threads producer. start(); consumer. start(); } } // end class Shared. Cell

Produce. Integer setting shared. Int to 1 Consume. Integer retrieving shared. Int value Produce. Integer setting shared. Int to 2 Consume. Integer retrieving shared. Int value Produce. Integer setting shared. Int to 3 Consume. Integer retrieving shared. Int value Produce. Integer setting shared. Int to 4 Consume. Integer retrieving shared. Int value Produce. Integer setting shared. Int to 5 Consume. Integer retrieving shared. Int value Produce. Integer setting shared. Int to 6 Consume. Integer retrieving shared. Int value Produce. Integer setting shared. Int to 7 Consume. Integer retrieving shared. Int value Produce. Integer setting shared. Int to 8 Consume. Integer retrieving shared. Int value Produce. Integer setting shared. Int to 9 Consume. Integer retrieving shared. Int value Produce. Integer setting shared. Int to 10 Produce. Integer finished producing values Terminating Produce. Integer Consume. Integer retrieving shared. Int value Consume. Integer retrieved values totaling: Terminating Consume. Integer 1 2 3 4 5 6 7 8 9 10 55
- Slides: 55