Lecture # 15: Java Multi-threading
SE 241 Advance Computer Programming
Muhammad Imran adopted from Dr. Usman Nasir
Multitasking
In computers, multitasking is the
concurrent execution of multiple tasks
(also known as processes) over a certain
period of time.
The tasks share common processing resources
This way, several processes can be
running on one CPU at same time.
Multitasking in Java
An effect of parallel execution by creating
multiple threads
Threads are like the processes, but are
light-weight, that is, they do not require
as much overhead and operating system
resources as full blown processes
Java Thread & Multithreading
In Java a thread is a
lightweight process
and has it own
separate path of
execution.
Each thread represents
its own flow of logic,
gets separate runtime
stack
A multithreaded
program contains two
or more threads that
can run concurrently.
Multithreading in Java
The JVM (a process) executes each thread
in the program for a short amount of time
Threads are largely managed by the JVM
As everything in Java belongs to a class,
threading is integrated into objects
New threads are created when objects are
started in their own thread
Same code for every Java VM
Simpler than in most other languages
Creating and Starting Threads
Creating a thread in Java is done by
making an object of Thread class and
calling its start method.
Thread thread = new Thread();
thread.start()
Thread class implements a generic thread
that, by default, does nothing
There are two techniques for creating and
running a threads in Java :
Inheriting from Thread class and overriding the
run method
Implementing Runnable interface to your class.
Inheriting Thread class
public class ThreadExample extends Thread {
public ThreadTest(String name) {
super(name);
System.out.println("Created " + name);
}
public void run( ) {
System.out.println(getName()+" started");
try {
Thread.sleep((long)(Math.random() * 1000));
} catch (InterruptedException e) { }
System.out.println(getName() + “ finishing");
}
}
public class ThreadDemo {
public static void main(String args[]) {
ThreadTest[] tt = new ThreadTest[3];
tt[0] = new ThreadTest("Thread 1");
tt[1] = new ThreadTest(“Thread 2”);
tt[2] = new ThreadTest(“Thread 3”);
System.out.println(“Finished creating threads");
tt[0].start();
tt[1].start();
tt[2].start();
System.out.println(“Finished starting threads");
}
}
The exact output depends on your CPU & JVM. In executing
thread and finishing.
> java ThreadDemo
Created Thread 1
Created Thread 2
Created Thread 3
Finished creating threads
Thread 1 started
Thread 2 started
Thread 3 started
Finished starting threads
Thread 3 finishing
Thread 1 finishing
Thread 2 finishing
Implementing Runnable Interface
The second way to is to write a class that
implements the java.lang.Runnable
interface.
A Java object that implements the
Runnable interface can be executed by a
Java Thread.
Runnable interface only has a single
method
public void run( )
When creating and starting a thread for
runnable reference a common mistake is
to call the run() method of the Thread
instead of start()
Thread newThread = new
Thread(MyRunnable());
newThread.run(); //should be start();
The run( ) method is executed but not
executed by the new thread.
Implementing Runnable Interface
public class ThreadTestRun implements Runnable {
String name;
public ThreadTest2(String name) {
this.name = name;
System.out.println(“Created “ + name);
}
public void run( ) {
System.out.println(name + “ started");
try {
Thread.sleep((long)(Math.random() * 1000));
} catch (InterruptedException e) { }
System.out.println(name + “ finishing");
}
}
public class ThreadDemo2 {
public static void main(String args[]) {
ThreadTestRun one= new ThreadTestRun
("Thread 1");
ThreadTestRun two= new ThreadTestRun
("Thread 1");
System.out.println(“Finished creating
threads");
Thread t= new Thread(one);
t.start();
new Thread(two).start();
System.out.println(“Finished starting
threads");
}
}
Java Thread Model
Java run-time system depends on threads to reduce
inefficiency by preventing the waste of CPU cycles.
A thread in Java exist in several states:
New
When we create an instance of Thread class, a thread is in a
new state.
Running
The Java thread is in running state.
Suspended
A running thread can be suspended, which temporarily
suspends its activity.
Blocked
A Java thread can be blocked when waiting for a resource.
Terminated
A thread can be terminated, which halts its execution
immediately at any given time. Once a thread is terminated, it
cannot be resumed.
The life cycle of a thread
start
Thread State: New
After creating an instance Thread, the
thread is in new thread state
ThreadTest tt = new ThreadTest("Thread 1");
When a thread is in new state, it is merely an
empty Thread object and no system resources
have been allocated to it.
In this state, you can only start the thread.
Calling any method besides start when a
thread is in this state makes no sense and
causes an IllegalThreadStateException.
Thread State: Running
Once start method is invoked on a thread,
the thread goes into running state
tt.start( );
Start method creates the system resources
necessary to run the thread, schedules the
thread to run, and calls the thread's run
method
Time Slicing & Thread Scheduler
The thread scheduler runs each thread for
a short amount of time called a time slice.
Then the scheduler picks another thread
from those that are runnable.
A thread is runnable if it is not asleep or
blocked in some way
There is no guarantee about the order in
which threads are executed
Thread Not Runnable
A thread becomes Not Runnable when one
of these events occurs:
Its sleep method is invoked
The thread calls the wait method to wait for a
specific condition to be satisfied
The thread is blocking on I/O
For each entrance into the Not Runnable
state, there is a specific escape route that
returns the thread to the Runnable state
Escape Routes
If a thread has been put to sleep, then the
specified number of milliseconds must
elapse
If a thread is waiting for a condition, then
another object must notify the waiting
thread of a change in condition by calling
notify or notifyAll
If a thread is blocked on I/O, then the I/O
must complete.
Stoping a Thread
A thread terminates when its run method finishes.
Program are recommended that they do not terminate
a thread using the deprecated stop method rather use
a method interrupt()
This notifies the thread that it should terminate.
Thus a thread's run method should check occasionally
whether it has been interrupted by checking the
isInterrupted() method
An interrupted thread should release resources, clean up,
and exit
The sleep method throws an InterruptedException
when a sleeping thread is interrupted
Catch the exception and then terminate the thread
public class Threading extends Thread {
public void run() {int count = 0;
System.out.println(getName() + " started");
System.out.println("Working in " +
Thread.currentThread().getName());
for (int i = 0; i < 10000000 && !isInterrupted(); i++) { count++;}
System.out.println(getName() + " finishing and count is: " + count);
}
public static void main(String args[]) throws Exception {
Threading one = new Threading("Thread 1");
Threading two = new Threading("Thread 2");
one.start(); two.start();
Thread.sleep(7);//Puts main to sleep for 07 milli seconds only
one.interrupt();
}
}//end of class
Output:
Thread 1 started
Thread 2 started
Working in Thread 2
Working in Thread 1
Thread 1 finishing and count is: 34150
Thread 2 finishing and count is: 10000000
Java Memory Model and Threads
Each thread running in the Java virtual
machine has its own thread stack that
contains information about what methods the
thread has called to reach the current point of
execution.
The thread stack also contains all local
variables for each method being executed. A
thread can only access it's own thread stack.
Local variables created by a thread are invisible to
all other threads than the thread who created it.
All local variables of primitive types are fully stored
on the thread stack and are thus not visible to
other threads.
Critical section & Race condition
Critical section is a section of code that is
executed by multiple threads and where
the sequence of execution for the threads
makes a difference in the result of the
concurrent execution of the critical section.
Race condition is the situation where two
threads compete for the same resource
and the sequence in which the resource is
accessed is significant.
Preventing Race Conditions
To prevent race conditions from occurring
programmer must make sure that the critical
section is executed as an atomic instruction.
Once a single thread is executing instruction, no
other threads can execute that instruction until the
first thread has left the critical section.
Race conditions can be avoided by proper
thread synchronization in critical sections.
Java synchronized block
Synchronized block marks a method or a
block of code as synchronized that helping
avoiding race conditions.
Synchronized blocks in Java are marked with
the synchronized keyword.
A synchronized block in Java is synchronized on
some object.
The synchronized synchronized keyword can
be used to mark four different types of blocks:
Instance methods
Static methods
Code blocks inside instance methods
Code blocks inside static methods
Method add( ) is synchronized.
public synchronized void add(int value){
this.count += value;
}
Synchronized Static methods:
public static synchronized void add(int
value){
count += value;
}
Synchronized Blocks in Instance Methods
You do not have to synchronize a whole method,
some part of code can be synchronized inside
methods
Synchronized block inside an unsynchronized
method:
public void add(int value){
synchronized(this){
this.count += value;
}
}
Synchronized Blocks in Static Methods
public class MyClass {
public static synchronized void log1(String msg1, String
msg2){
log.writeln(msg1);
log.writeln(msg2);
}
public static void log2(String msg1, String msg2){
synchronized(MyClass.class){
log.writeln(msg1);
log.writeln(msg2);
}
}
}
Bank Application (Example)
Create a BankAccount object
Create a DepositThread t0 to deposit $100
into the account for 10 iterations
Create a WithdrawThread t1 to withdraw
$100 from the account for 10 iterations
The result should be zero, but on
sometimes it is not
BankAccount.java
public BankAccount() {
balance = 0;
}
public BankAccount(double initDepo) {
balance = initDepo;
}
public void deposit(double amount) {
double newBalance = balance + amount;
System.out.println("Depositing " + amount +", new
balance is " + newBalance);
balance = newBalance;}
}
BankAccount.java
public void withdraw(double amount) {
double newBalance = balance - amount;
System.out.println("Withdrawing " +
amount +", new balance is "+ newBalance);
balance = newBalance;
}
public double getBalance() { return balance;}
}//end of class
DepositThread.java
public class DepositThread extends Thread {
public DepositThread(BankAccount anAccount, double anAmount) {
account = anAccount; amount = anAmount; }
public void run() {
try {
for (int i = 1; i <= REPETITIONS && !isInterrupted();i++) {
account.deposit(amount); sleep(DELAY);
}
} catch (InterruptedException exception) { }
}
private BankAccount account;
private double amount; private static final int REPETITIONS = 10;
private static final int DELAY = 10;
}
Driver
BankAccount acc = new BankAccount(0);
System.out.println(acc.getBalance());
DepositThread deposits = new DepositThread(acc,
100);
WithdrawThread withdraws = new
WithdrawThread(acc, 100);
deposits.start(); withdraws.start();
Thread.sleep(1000);
System.out.println("Closing Bal now: " +
acc.getBalance());
Output
0.0
Depositing 100.0, new balance is 100.0
Withdrawing 100.0, new balance is -100.0
Withdrawing 100.0, new balance is -200.0
Depositing 100.0, new balance is 0.0
Depositing 100.0, new balance is 100.0
……………
……………
Withdrawing 100.0, new balance is 0.0
Depositing 100.0, new balance is 200.0
Withdrawing 100.0, new balance is 200.0
Depositing 100.0, new balance is 400.0
Closing Bal now: 400.0
The first thread t0 executes the lines
double newBalance = balance + amount;
t0 reaches the end of its time slice and t1
gains control
t1 calls the withdraw method which
withdraws $100 from the balance variable.
Balance is now -100
t1 goes to sleep and t0 regains control
and picks up where it left off.
t0 executes next lines
Synchronized Methods
public class BankAccount {
public synchronized void deposit(double
amount) {
...
}
public synchronized void withdraw(double
amount) {
...
}
...
}
By declaring both the deposit and withdraw
methods to be synchronized
Only one thread at a time can execute either
method on a given object
When a thread starts one of the methods, it is
guaranteed to execute the method to completion
before another thread can execute a
synchronized method on the same object.
Thank you
Very good tutorial with a lot of details:
http://tutorials.jenkov.com/java-
concurrency/index.html