CPSC 441 MultiThreading in Java Department of Computer

  • Slides: 28
Download presentation
CPSC 441 Multi-Threading in Java Department of Computer Science University of Calgary

CPSC 441 Multi-Threading in Java Department of Computer Science University of Calgary

Threading Mechanisms r Create a class that extends the Thread class r Create a

Threading Mechanisms r Create a class that extends the Thread class r Create a class that implements the Runnable interface Thread Runnable My. Thread My. Class (objects are threads) (objects with run() body) [a] [b] Thread

1 st method: Extending Thread class r Create a class by extending Thread class

1 st method: Extending Thread class r Create a class by extending Thread class and override run() method: class Extend. Thread extends Thread { public void run() { // thread body of execution } } r Create a thread: Extend. Thread thread = new Extend. Thread(); r Start Execution of threads: thread. start();

Template class Ext. Thread extends Thread { public void run() { System. out. println("running");

Template class Ext. Thread extends Thread { public void run() { System. out. println("running"); } } class Main { public static void main(String [] args) { Ext. Thread t = new Ext. Thread(); t. start(); } }

2 nd method: implementing Runnable interface r Create a class that implements the interface

2 nd method: implementing Runnable interface r Create a class that implements the interface Runnable and override run() method: class Impl. Thread implements Runnable { public void run() { // thread body } } r Create an object of the class: Impl. Thread impl = new Impl. Thread(); r Create a thread object that takes the Runnable as a parameter: Thread thread = new Thread(impl); r Start the thread: thread. start();

Template class Thread. Impl implements Runnable { public void run() { // thread body

Template class Thread. Impl implements Runnable { public void run() { // thread body } } class Main { public static void main(String [] args) { Thread t = new Thread(new Thread. Impl()); t. start(); } }

Example of Java Threads import java. util. concurrent. *; public class Thread. Example implements

Example of Java Threads import java. util. concurrent. *; public class Thread. Example implements Runnable { private String greeting; Returns name public Thread. Example(String greeting) { Returns reference of thread as this. greeting = greeting; string to current thread } public void run( ) { while(true) { System. out. println(Thread. current. Thread( ). get. Name( ) + ": "+greeting); Suspend the thread; try { Thread sleeps for Time. Unit. MILLISECONDS. sleep(((long) Math. random( ) * 100)); random amount of } catch(Interrupted. Exception e) {} time. } 1. Create new instance of } Thread. Example with different public static void main(String [ ] args) { message new Thread(new Thread. Example("Message 1")). start( ); new Thread(new Thread. Example("Message 2")). start( ); 2. Passes new instance to the new Thread(new Thread. Example("Message 3")). start( ); constructor of Thread. } 3. Calls new Thread instance's } start() Each thread independently executes run() of Thread. Example, while the main thread terminates. Upon execution an interleaving of three greeting messages is printed

Multi-Threading Example class My. Runnable implements Runnable { private final long count. Until; My.

Multi-Threading Example class My. Runnable implements Runnable { private final long count. Until; My. Runnable(long count. Until) { this. count. Until = count. Until; } public void run() { long sum = 0; for (long i = 1; i < count. Until; i++) { sum += i; } System. out. println("Worker"+ Thread. current. Thread(). get. Id() +"out. "); } } Used to store import java. util. Array. List; the threads to public class Main { check if they are done public static void main(String args[]) { Array. List<Thread> threads = new Array. List<Thread>(); for (int i = 0; i < 500; i++) { Runnable task = new My. Runnable(10000000 L + i); Thread worker = new Thread(task); worker. set. Name(String. value. Of(i)); worker. start(); try { worker. join(); //joins after each thread is created -> sequential execution } catch (Interrupted. Exception e) { System. out. println("Thread" + Thread. current. Thread(). get. Name() + "Interrupted. "); } threads. add(worker); } Store thread for later usage } }

Executor Service import java. util. concurrent. Executor. Service; The executor service import java. util.

Executor Service import java. util. concurrent. Executor. Service; The executor service import java. util. concurrent. Executors; provides methods to import java. util. concurrent. Time. Unit; keep track of threads public class Executor. Test { and manage them. private static final int NTHREDS = 10; public static void main(String[] args) { Executor. Service executor = Executors. new. Fixed. Thread. Pool(NTHREDS); for (int i = 0; i < 500; i++) { This will make the My. Runnable worker = new My. Runnable(10000000 L + i); executor accept no executor. execute(worker); new threads and finish } all existing threads in try { the queue executor. shutdown(); Wait until all threads executor. await. Termination(10, Time. Unit. SECONDS); are finished } catch (Interrupted. Exception e) { System. out. println(" Thread" + Thread. current. Thread(). get. Id() + "Interrupted. "); } System. out. println("Finished all threads"); } }

How to pause a thread r Sleep suspends a thread temporarily r Invoke the

How to pause a thread r Sleep suspends a thread temporarily r Invoke the static method sleep(milliseconds) of the Thread class: v Must be called from current Thread, e. g. in its run() method try { Thread. sleep(2000); } catch (Interrupted. Exception ex) { // code to resume or terminate. . . }

Interrupting a thread r Can be used to stop or resume the execution of

Interrupting a thread r Can be used to stop or resume the execution of that thread from another thread. v Interrupts the thread t 1 from the current thread: t 1. interrupt(); r If a thread is sleeping, then calling interrupt() on it will cause an Interrupted. Exception to be thrown. v Whether a thread should stop or resume depends on the Interrupted. Exception exception handler.

Example The thread t 1 prints a message after every 2 seconds, and the

Example The thread t 1 prints a message after every 2 seconds, and the main thread interrupts t 1 after 5 seconds: public class Thread. Interrupt. Example implements Runnable { public void run() { for (int i = 0; i < 10; i++) { System. out. println("This is message #" + (i+1)); public static void main(String[] args) { Thread t 1 = new Thread(new Thread. Interrupt. Example()); t 1. start(); try { Thread. sleep(5000); t 1. interrupt(); } catch (Interrupted. Exception ex) { // do nothing } try { Thread. sleep(2000); } catch (Interrupted. Exception ex) { System. out. println("resuming"); } } } 12

Explanation r In the catch block in the run() method, it continues the for

Explanation r In the catch block in the run() method, it continues the for loop when the thread is interrupted: try { Thread. sleep(2000); } catch (Interrupted. Exception ex) { System. out. println("resuming"); //continues execution } r To stop the thread, just change the code in the catch block to return from the run() method. try { Thread. sleep(2000); } catch (Interrupted. Exception ex) { return; //about to stop due to early exit } 13

Join(), or making threads wait for other threads r Joining is useful in case

Join(), or making threads wait for other threads r Joining is useful in case you want the current thread to wait for other threads to complete. r After that, the current thread continues running. For example: t 1. join(); r This causes the current thread to wait for the thread t 1 to complete before it continues. 14

Other forms of join() r There are 2 overloads of join() method: v v

Other forms of join() r There are 2 overloads of join() method: v v - join(milliseconds) - join(milliseconds, nanoseconds) r These methods cause the current thread to wait (at most) the specified time. v If the time expires and the joined thread has not completed, the current thread continues running normally. r You can also join multiple threads with the current thread, for example: t 1. join(); t 2. join(); t 3. join(); v In this case, the current thread must wait for all three threads t 1, t 2 and t 3 completes before it can resume running. 15

Example Here, the current thread (main) waits for the thread t 1 to complete:

Example Here, the current thread (main) waits for the thread t 1 to complete: public class Thread. Join. Example implements Runnable { This is message #1 This is message #2 This is message try { #3 t 1. join(); This is message } catch (Interrupted. Exception ex) { } #4 System. out. println("I'm " + Thread. current. Thread(). get. Name()); This is message } #5 } This is message #6 This is message #7 This is message 16 #8 public static void main(String[] args) { public void run() { Thread t 1 = new Thread(new Thread. Join. Example()); for (int i = 1; i <= 10; i++) { t 1. start(); System. out. println("This is message #" + i); try { Thread. sleep(2000); } catch (Interrupted. Exception ex) { return; } } } In this program, the current thread (main) always terminates after the thread t 1 completes. The message “I’m main” is always printed last:

Thread Interference r Consider a simple class called Counter r If multiple threads attempt

Thread Interference r Consider a simple class called Counter r If multiple threads attempt to access the same Counter object, then it can lead to different values due to thread interference. r Consider the case where 1 thread increments c, another decrements it, 4 threads read its value. r The outputs can be any of: [0, 0, 0, 0], [0, 0, 1, 1], [0, 1, 0, 0], [1, 1, 0, 0], [-1, 0, 0], [0, -1, 0, 0], all the way to [-1, -1, -1] v For example, in the second case, the thread that increments c can occur after the first two threads have printed c's value, and the thread that decrements c can occur last. class Counter { private int c = 0; public void increment() { c++; } public void decrement() { c--; } public int value() { return c; } } 17

Synchronization r Synchronization allows only one thread to perform an operation on an object

Synchronization r Synchronization allows only one thread to perform an operation on an object at a time. r If multiple threads require an access to an object, synchronization helps maintain consistency. r The Java programming language provides two basic synchronization idioms: v v synchronized methods synchronized statements. 18

Intrinsic Locks and Synchronization r Monitor (lock) r Used for synchronization r Enforces exclusive

Intrinsic Locks and Synchronization r Monitor (lock) r Used for synchronization r Enforces exclusive access and order of relationships r Lock acquisition and release – required for exclusive access r A thread that acquires a lock owns it r A lock that is owned cannot be acquired by other threads v Threads that try to do so will be blocked 19

Synchronized Methods r When a thread invokes a synchronized method, it automatically acquires the

Synchronized Methods r When a thread invokes a synchronized method, it automatically acquires the intrinsic lock for that method's object and releases it when the method returns. v The lock release occurs even if the return was caused by an uncaught exception. r To make a method synchronized, simply add the synchronized keyword to its declaration, as seen in the Synchronized. Counter class. public class Synchronized. Counter { private int c = 0; public synchronized void increment() { c++; } public synchronized void decrement() { c--; } public synchronized int value() { return c; } } 20

Synchronized Statements r Unlike synchronized methods, synchronized statements must specify the object that provides

Synchronized Statements r Unlike synchronized methods, synchronized statements must specify the object that provides the intrinsic lock: public void add. Name(String name) { synchronized(this) { last. Name = name; name. Count++; } name. List. add(name); } 21

Example r In this example, the add. Name() method needs to synchronize changes to

Example r In this example, the add. Name() method needs to synchronize changes to last. Name() and name. Count(), but also needs to avoid synchronizing invocations of other objects' methods. public class Ms. Lunch { private long c 1 = 0; private long c 2 = 0; private Object lock 1 = new Object(); private Object lock 2 = new Object(); public void inc 1() { synchronized(lock 1) { c 1++; } } public void inc 2() { synchronized(lock 2) { c 2++; } } } 22

Example In this example, the counter tells how many times an access to count

Example In this example, the counter tells how many times an access to count was made. r If a thread is accessing set. Count (and updating count) and another thread is accessing get. Count at the same time, there will be inconsistency in the value of count. r public class Counter{ private int count = 0; public int get. Count(){ return count; } public set. Count(int count){ this. count = count; } }

Fixing the example By adding the synchronized keyword we make sure that when one

Fixing the example By adding the synchronized keyword we make sure that when one thread is in the set. Count method the other threads are all in the waiting state. r The synchronized keyword places a lock on the object, and hence locks all the other methods which have the keyword synchronized. r public class Counter{ private static int count = 0; public synchronized int get. Count(){ return count; } public synchronized set. Count(int count){ this. count = count; } }

Object locking r The synchronized keyword locks the object. r The wait keyword waits

Object locking r The synchronized keyword locks the object. r The wait keyword waits for the lock to be acquired, if the object was already locked by another thread. r notify. All() notifies other threads that the lock is about to be released by the current thread. • Another method, notify() is available for use, which wakes up only the next thread which is in queue for the object, notify. All() wakes up all the threads and transfers the lock to another thread having the highest priority. synchronized(my. Instance){ try{ wait(); } catch(Interrupted. Exception ex){ } System. out. println("Within block"); notify. All(); }

Multithreading Example without Synchronization class Print. Demo { public void print. Count() { try

Multithreading Example without Synchronization class Print. Demo { public void print. Count() { try { for(int i = 5; i > 0; i--) { String print_out = "Counter ---"; for (int j = 0; j < print_out. length(); ++j) { System. out. print(print_out. char. At(j)); } System. out. println("Counter --- " + i ); } } catch (Exception e) { System. out. println("Thread interrupted. "); } } } public void start () { System. out. println("Starting " + thread. Name ); if (t == null) { t = new Thread (this, thread. Name); t. start (); } } } public class Test. Thread { public static void main(String args[]) { Print. Demo PD = new Print. Demo(); Thread. Demo T 1 = new Thread. Demo( "Thread - 1 ", PD ); Thread. Demo T 2 = new Thread. Demo( "Thread - 2 ", PD ); T 1. start(); T 2. start(); class Thread. Demo extends Thread { private Thread t; private String thread. Name; Print. Demo PD; Thread. Demo( String name, Print. Demo pd) { thread. Name = name; PD = pd; } public void run() { PD. print. Count(); System. out. println("Thread " + thread. Name + " exiting. "); } // wait for threads to end try { T 1. join(); T 2. join(); } catch ( Exception e) { System. out. println("Interrupted"); } r Here is a simple example which may or may not print counter value in sequence and every time we run it, it produces a different result based on CPU availability to a thread. Starting Thread - 1 Starting Thread - 2 Counter ---5 Counter ---4 Counter ---3 Counter ---2 Counter er ---1 Thread - 1 exiting. ---5 Counter ---4 Counter ---3 Counter ---2 Counter ---1 Thread - 2 exiting. } } 26

With Synchronization class Print. Demo { public void print. Count() { try { for(int

With Synchronization class Print. Demo { public void print. Count() { try { for(int i = 5; i > 0; i--) { String print_out = "Counter ---"; for (int j = 0; j < print_out. length(); ++j) { System. out. print(print_out. char. At(j)); } System. out. println("Counter --- " + i ); } } catch (Exception e) { System. out. println("Thread interrupted. "); } } } public void run() { synchronized(PD) { PD. print. Count(); } System. out. println("Thread " + thread. Name + " exiting. "); } public void start () { System. out. println("Starting " + thread. Name ); if (t == null) { t = new Thread (this, thread. Name); t. start (); } } } public class Test. Thread { public static void main(String args[]) { Print. Demo PD = new Print. Demo(); Thread. Demo T 1 = new Thread. Demo( "Thread - 1 ", PD ); Thread. Demo T 2 = new Thread. Demo( "Thread - 2 ", PD ); class Thread. Demo extends Thread { private Thread t; private String thread. Name; Print. Demo PD; Thread. Demo( String name, Print. Demo pd) { thread. Name = name; PD = pd; } public void run() { PD. print. Count(); System. out. println("Thread " + thread. Name + " exiting. "); } T 1. start(); T 2. start(); // wait for threads to end try { T 1. join(); T 2. join(); } catch ( Exception e) { System. out. println("Interrupted"); } } } r Here is the same example which prints counter value in sequence and every time we run it, it produces the same result. Starting Thread - 1 Starting Thread - 2 Counter ---5 Counter ---4 Counter ---3 Counter ---2 Counter ---1 Thread - 1 exiting. Counter ---5 Counter ---4 Counter ---3 Counter ---2 Counter ---1 Thread - 2 exiting. 27

Immutable Objects r An object is considered immutable if its state cannot change after

Immutable Objects r An object is considered immutable if its state cannot change after it is constructed. r Immutable objects are particularly useful in concurrent applications. Since they cannot change state, they cannot be corrupted by thread interference or observed in an inconsistent state. v Don't add any setter method Declare all fields final and private v Don't allow subclasses to override methods. v r See also: functional programming (Scala) 28