CPSC 441 Multi-Threading in Java

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

Transcript of CPSC 441 Multi-Threading in Java

CPSC 441Multi-Threading in Java

Department of Computer ScienceUniversity of Calgary

2

Threading Mechanisms

rCreate a class that extends the Thread classrCreate a class that implements the Runnable

interface

Thread

MyThread

Runnable

MyClass

Thread

(objects are threads) (objects with run() body)

[a] [b]

3

1st method: Extending Thread class

r Create a class by extending Thread class and override run() method:

class MyThread extends Thread{

public void run(){

// thread body of execution}

}r Create a thread:

MyThread thr1 = new MyThread();r Start Execution of threads:

thr1.start();r Create and Execute:

new MyThread().start();

4

Template

class MyThread extends Thread { public void run() {

System.out.println(" this thread is running ... ");}

}

class ThreadEx1 { public static void main(String [] args ) {

MyThread t = new MyThread();t.start();

}}

5

2nd method: implementing Runnable interface

r Create a class that implements the interface Runnable and override run() method:

class MyThread implements Runnable{

.....public void run(){

// thread body of execution}

}r Creating Object:

MyThread myObject = new MyThread();r Creating Thread Object:

Thread thr1 = new Thread( myObject );r Start Execution:

thr1.start();

6

Template

class MyThread implements Runnable {public void run() {

System.out.println(" this thread is running ... ");}

}

class ThreadEx2 {public static void main(String [] args ) {

Thread t = new Thread(new MyThread());t.start();

} }

Example of Java Threads

public class ThreadExample implements Runnable {private String greeting;public ThreadExample(String greeting) {

this.greeting = greeting;}public void run( ) {

while(true) {System.out.println(Thread.currentThread( ).getName( ) + ": "+greeting);

try {TimeUnit.MILLISECONDS.sleep(((long) Math.random( ) * 100));

} catch(InterruptedException e) {}

}}public static void main(String [ ] args) {

new Thread(new ThreadExample(“Greeting 1")).start( );new Thread(new ThreadExample(“Greeting 2")).start( );new Thread(new ThreadExample(“Greeting 3")).start( );

}}

Returns reference to current thread Returns name

of thread as string

Suspend the thread; Thread sleeps for random amount of time.

1. Create new instance of ThreadExample with different greeting

2. Passes new instance to the constructor of Thread.

3. Calls new Thread instance's start()

Each thread independently executes run() of ThreadExample, while the main thread terminates. Upon execution an interleaving of three greeting messages is printed

3-7

Multi-ThreadingExample – Keep Trackclass MyRunnable implements Runnable {

private final long countUntil;

MyRunnable(long countUntil) {this.countUntil = countUntil;

}

public void run() {long sum = 0;for (long i = 1; i < countUntil; i++) {

sum += i;}

System.out.println("Worker"+Thread.currentThread().getId() +"out.");

}

}

8

Public class Main {

public static void main (String args[]) {List<Thread> threads = new ArrayList<Thread>();

for (int i = 0; i < 500; i++) {

Runnable task = new MyRunnable(10000000L+i);

Thread worker = new Thread(task);worker.setName(String.valueOf(i));worker.start();try{

worker.join()}

catch (InterruptedException e) {System.out.println(" Thread”+Thread.currentThread().getId()+Interrupted.")

}threads.add(worker);

}

}}

MyRunnable is the task which will be performed

We will store the threads so that we can check if they are done

Remember the thread for later usage

Executor Service

import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Main {

private static final int NTHREDS = 10;public static void main(String[] args) {

ExecutorService executor = Executors.newFixedThreadPool(NTHREDS);for (int i = 0; i < 500; i++) {

Runnable worker = new MyRunnable(10000000L + i);executor.execute(worker);

}try{

executor.shutdown();executor.awaitTermination(10, TimeUnit.SECONDS);

}

catch (InterruptedException e) {System.out.println(" Thread”+Thread.currentThread().getId()+Interrupted.")

}System.out.println("Finished all threads");

}} 9

This will make the executor accept no new threads and finish all existing threads in the queue

Wait until all threads are finished, or the timeout occurs

The executor service provides methods to keep track of threads and manage them.

How to pause a thread

r Invoke the static method sleep(milliseconds) of the Thread class:

10

try {Thread.sleep(2000);

} catch (InterruptedException ex) {// code to resume or terminate...

}

How to interrupt a thread

r Can be used to stop or resume the execution of that thread from another thread. v Interrupts the thread t1 from the current thread:

r If t1 is sleeping, then calling interrupt() on t1 will cause the InterruptedException to be thrown. v And whether the thread should stop or resume depending on the

handling code in the catch block.

11

t1.interrupt();

r Ex: The thread t1 prints a message after every 2 seconds, and the main thread interrupts t1 after 5 seconds:

12

public class ThreadInterruptExample implements Runnable {

public void run() {for (int i = 1; i <= 10; i++) {

System.out.println("This is message #" + i);

try {Thread.sleep(2000);

} catch (InterruptedException ex) {System.out.println("I'm resumed");continue;

}}

}

public static void main(String[] args) {Thread t1 = new Thread(new ThreadInterruptExample());t1.start();

try {Thread.sleep(5000);t1.interrupt();

} catch (InterruptedException ex) {// do nothing

}

}}

This is message #1This is message #2This is message #3I'm resumedThis is message #4

This is message #5This is message #6This is message #7This is message #8This is message #9

This is message #10

r Ex: In the catch block in the run() method, it continues the for loop when the thread is interrupted:

r That means the thread resumes running while it is sleeping.r To stop the thread, just change the code in the catch block to

return from the run() method like this:

13

try {Thread.sleep(2000);

} catch (InterruptedException ex) {System.out.println("I'm resumed");continue;

}

try {

Thread.sleep(2000);} catch (InterruptedException ex) {

System.out.println("I'm about to stop");return;

}

How to make a thread wait for other threads (join)?

r This is called joining and 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:

v Causes the current thread to wait for the thread t1 to complete before it continues.

14

t1.join();

r Ex: Current thread (main) waits for the thread t1 to complete:

15

public class ThreadJoinExample implements Runnable {

public void run() {for (int i = 1; i <= 10; i++) {

System.out.println("This is message #" + i);

try {Thread.sleep(2000);

} catch (InterruptedException ex) {System.out.println("I'm about to stop");return;

}}

}

public static void main(String[] args) {Thread t1 = new Thread(new ThreadJoinExample());t1.start();

try {

t1.join();

} catch (InterruptedException ex) {// do nothing

}

System.out.println("I'm " + Thread.currentThread().getName());}

}

In this program, the current thread (main) always terminates after the thread t1 completes. Hence you see the message “I’m main” is always printed last:

This is message #1

This is message #2This is message #3This is message #4This is message #5This is message #6This is message #7

This is message #8This is message #9This is message #10I'm main

r There are 2 overloads of join() method:v - join(milliseconds)v - join(milliseconds, nanoseconds)

r These methods cause the current thread to wait at most for the specified time. That means 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:

v In this case, the current thread has to wait for all three threads t1, t2 and t3 completes before it can resume running.

16

t1.join();t2.join();t3.join();

Thread Interferencer Consider a simple class called Counter

17

class Counter {private int c = 0;

public void increment() {c++;

}

public void decrement() {c--;

}

public int value() {return c;

}

}

r Synchronization is prevent data corruptionr Synchronization allows only one thread to perform an

operation on a object at a time.r If multiple threads require an access to an object,

synchronization helps in maintaining consistency.

r The Java programming language provides two basic synchronization idioms:v synchronized methodsv synchronized statements (blocks)

18

Synchronized Methodsr 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:

19

public class SynchronizedCounter {private int c = 0;

public synchronized void increment() {c++;

}

public synchronized void decrement() {c--;

}

public synchronized int value() {

return c;}

}

Synchronized Statements

20

r Unlike synchronized methods, synchronized statements must specify the object that provides the intrinsic lock:

public void addName(String name) {synchronized(this) {

lastName = name;nameCount++;

}nameList.add(name);

}

Intrinsic Locks and Synchronizationr Synchronization is built around an internal entity known as the intrinsic lock or monitor lock. (The

API specification often refers to this entity simply as a "monitor.") r Intrinsic locks play a role in both aspects of synchronization: enforcing exclusive access to an

object's state and establishing happens-before relationships that are essential to visibility.r Every object has an intrinsic lock associated with it. r A thread that needs exclusive and consistent access to an object's fields has to acquire the

object's intrinsic lock before accessing them, and then release the intrinsic lock when it's done with them.

r A thread is said to own the intrinsic lock between the time it has acquired the lock and released the lock.

r As long as a thread owns an intrinsic lock, no other thread can acquire the same lock. The other thread will block when it attempts to acquire the lock.

r When a thread releases an intrinsic lock, a happens-before relationship is established between that action and any subsequent acquisition of the same lock.

21

Example

public class Counter{private int count = 0;public int getCount(){

return count;}

public setCount(int count){this.count = count;

}}r In this example, the counter tells how many an access has been made.r If a thread is accessing setCount and updating count and another thread is accessing

getCount at the same time, there will be inconsistency in the value of count.

Fixing the example

public class Counter{private static int count = 0;public synchronized int getCount(){

return count;}

public synchoronized setCount(int count){this.count = count;

}}r By adding the synchronized keyword we make sure that when one thread is in the

setCount method the other threads are all in waiting state. r The synchronized keyword places a lock on the object, and hence locks all the other

methods which have the keyword synchronized. The lock does not lock the methods without the keyword synchronized and hence they are open to access by other threads.

What about static methods?

public class Counter{private int count = 0;public static synchronized int getCount(){

return count;}

public static synchronized setCount(int count){this.count = count;

}}r In this example the methods are static and hence are associated with the class object

and not the instance. r Hence the lock is placed on the class object that is, Counter.class object and not on

the object itself. Any other non static synchronized methods are still available for access by other threads.

Common Synchronization mistake

public class Counter{private int count = 0;public static synchronized int getCount(){

return count;}

public synchronized setCount(int count){this.count = count;

}}r The common mistake here is one method is static synchronized and another method is

non static synchronized.r This makes a difference as locks are placed on two different objects. The class object

and the instance and hence two different threads can access the methods simultaneously.

Multithreading Example without Synchronization

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.

26

class PrintDemo {public void printCount() {

try {for(int i = 5; i > 0; i--) {

System.out.println("Counter --- " + i );}

} catch (Exception e) {System.out.println("Thread interrupted.");

}}

}

class ThreadDemo extends Thread {private Thread t;private String threadName;PrintDemo PD;

ThreadDemo( String name, PrintDemo pd) {threadName = name;PD = pd;

}

public void run() {PD.printCount();System.out.println("Thread " + threadName + " exiting.");

}

public void start () {System.out.println("Starting " + threadName );if (t == null) {

t = new Thread (this, threadName);t.start ();

}}

}

public class TestThread {public static void main(String args[]) {

PrintDemo PD = new PrintDemo();

ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );

T1.start();T2.start();

// wait for threads to endtry {T1.join();T2.join();

} catch ( Exception e) {System.out.println("Interrupted");

}}

}

Starting Thread - 1Starting Thread - 2Counter --- 5Counter --- 4Counter --- 3Counter --- 5Counter --- 2Counter --- 1Counter --- 4Thread Thread - 1 exiting.Counter --- 3Counter --- 2Counter --- 1Thread Thread - 2 exiting.

Multithreading Example with Synchronization

r Here is the same example which prints counter value in sequence and every time we run it, it produces the same result.

27

class PrintDemo {public void printCount() {

try {

for(int i = 5; i > 0; i--) {System.out.println("Counter --- "

+ i );}

} catch (Exception e) {System.out.println("Thread

interrupted.");

}}

}

class ThreadDemo extends Thread {

private Thread t;private String threadName;PrintDemo PD;

ThreadDemo( String name, PrintDemopd) {

threadName = name;PD = pd;

}

public void run() {synchronized(PD) {

PD.printCount();

}

System.out.println("Thread " + threadName + " exiting.");}

public void start () {System.out.println("Starting " + threadName );if (t == null) {

t = new Thread (this, threadName);t.start ();

}}

}

public class TestThread {

public static void main(String args[]) {PrintDemo PD = new PrintDemo();

ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );

ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );

T1.start();T2.start();

// wait for threads to endtry {

T1.join();T2.join();

} catch ( Exception e) {

System.out.println("Interrupted");}

}}

Starting Thread - 1Starting Thread - 2Counter --- 5Counter --- 4Counter --- 3Counter --- 2Counter --- 1Thread Thread - 1 exiting.Counter --- 5Counter --- 4Counter --- 3Counter --- 2Counter --- 1Thread Thread - 2 exiting.