0% found this document useful (0 votes)
49 views42 pages

Concurrency

Threads allow concurrent execution of code within a process. A thread is a sequence of instructions that can run independently and concurrently within the same process. Threads allow for parallel execution and increase responsiveness in applications. Each thread has its own call stack and threads are managed independently by the scheduler.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
49 views42 pages

Concurrency

Threads allow concurrent execution of code within a process. A thread is a sequence of instructions that can run independently and concurrently within the same process. Threads allow for parallel execution and increase responsiveness in applications. Each thread has its own call stack and threads are managed independently by the scheduler.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 42

CONCURRENCY

Threads

Thread of execution is a sequence of programmed instructions


that are part of a process
Program Program
(Process) (Process)

A thread is managed independently by a scheduler, which is


typically a part of the operating system.
Threads vs Process

Similarities:
- Concurrency
- Execution planning

Differences:
- One thread can exist only inside of a process
- Process = code + data
- Thread = code
Defining Threads

A thread is an instance of class java.lang.Thread

An instance of Thread is just…an object.


Like any other object in Java, it has variables and methods, and lives and dies on the
heap. But a thread of execution has its own call stack. In Java, there is one call stack
per thread.

The main() method, runs in one thread, called (surprisingly) the main thread. If you
look at the main call stack (in debugging mode), you'd see that main() is the first
method on the stack—the method at the bottom.

As soon as you create a new thread, a new stack materializes and methods called from
that thread run in a call stack that's separate from the main() call stack. That second
new call stack is said to run concurrently.
Defining Threads

Defining a thread:
- extend class Thread
- implement interface Runnable

Runnable
- Defines a common protocol for active objects
- Contains method run() - job always starts from a run() method
- It is implemented by class Thread
Defining Threads

Extending java.lang.Thread
The simplest way to define code to run in a separate thread is to
- Extend the java.lang.Thread class.
- Override the run() method.

class MyThread extends Thread {


public void run() {
System.out.println("Important job running in
MyThread");
}
}
Defining Threads

• Keep in mind that you're free to overload the run() method in your Thread
subclass:

class MyThread extends Thread {


public void run() {
System.out.println("Important job running
in MyThread");
}

public void run(String s) {


System.out.println("String in run is " + s);
}
}
Defining Threads

• The overloaded run(String s) method will be ignored by the Thread class unless
you call it yourself. The Thread class expects a run() method with no arguments,
and it will execute this method for you in a separate call stack after the thread has
been started.

• With a run(String s) method, the Thread class won't call the method for you, and
even if you call the method directly yourself, execution won't happen in a new
thread of execution with a separate call stack. It will just happen in the same call
stack as the code that you made the call from, just like any other normal method
call.
Instantiating Threads

Instantiating a thread:

MyThread myThread = new MyThread();


myThread.start();

If you call directly run() method, you don’t start a new thread with it’s own stack!!!
Defining Threads

Implementing java.lang.Runnable

class MyRunnable implements Runnable {


public void run() {
System.out.println("Important job running in
MyRunnable");
}
}
Instantiating Threads

Instantiating a thread:

MyRunnable r = new MyRunnable();


Thread t = new Thread(r); // Pass your Runnable to the Thread
t.start();

When calling start()


- A new thread of execution starts (with a new call stack).
- The thread moves from the new state to the runnable state.
- When the thread gets a chance to execute, its target run() method will run.
Threads – Call Stacks
Exercise

Create a simple counting thread. It will count from start to end with a certain step.
Create 3 instances of the thread, that will count from 1 to 100 with the following
steps: 2, 3 and 5.
Set a name for each thread and when printing the numbers, print also the name of the
thread.
Thread States

New - state of the thread after the Thread instance has been created, but the
start() method has not been invoke.
Runnable - state of the thread when it's eligible to run, but the scheduler has not
selected it to be the running thread. A thread first enters the runnable state when the
start() method is invoked, but a thread can also return to the runnable state after either
running or coming back from a blocked, waiting, or sleeping state.
Thread States

Running - state of a thread when the thread scheduler selects it (from the runnable
pool) to be the currently executing process.
There are several ways to get to the runnable state, but only one way to get to the
running state: the scheduler chooses a thread from the runnable pool.
Thread States

Waiting/blocked/sleeping - state of a thread when it's not eligible to run. It is not


runnable, but it might return to a runnable state later if a particular event occurs.
A thread may be blocked waiting for a resource (like I/O or an object's lock), in which
case the event that sends it back to runnable is the availability of the resource.
A thread may be sleeping because the thread's run code tells it to sleep for some
period of time, in which case the event that sends it back to runnable is that it wakes
up because its sleep time has expired.
Or the thread may be waiting, because the thread's run code causes it to wait, in
which case the event that sends it back to runnable is that another thread sends a
notification that it may no longer be necessary for the thread to wait
Thread States

Dead A thread is considered dead when its run() method completes.


Once a thread is dead, it can never be brought back to life. If you invoke start() on a
dead Thread instance, you'll get a runtime (not compiler) exception.
Thread States

Sleeping
The sleep() method is a static method of class Thread.
You use it in your code to "slow a thread down" by forcing it to go into a sleep mode
before coming back to runnable . When a thread sleeps, it drifts off somewhere and
doesn't return to runnable until it wakes up.
try {
Thread.sleep(5*60*1000); // Sleep for 5 minutes
} catch (InterruptedException ex) { }
Exercise

Create a simple counting thread. It will count to 100, pausing one second between
each number.
Also, it will output a string every ten numbers.
Thread Priorities

The scheduler does use thread priorities in one important way: If a thread enters the
runnable state, and it has a higher priority than any of the threads in the pool and a
higher priority than the currently running thread, the lower-priority running thread
usually will be bumped back to runnable and the highest-priority thread will be chosen
to run.
In other words, at any given time the currently running thread usually will not have a
priority that is lower than any of the threads in the pool. In most cases, the running
thread will be of equal or greater priority than the highest priority threads in the pool.
Thread Priorities

Setting a Thread's Priority


A thread gets a default priority that is the priority of the thread of execution that
creates it. For example, in the code

public class TestThreads {


public static void main (String [] args) {
MyThread t = new MyThread();
}
}
the thread referenced by t will have the same priority as the main thread.
You can also set a thread's priority directly by calling the setPriority() method:

FooRunnable r = new FooRunnable();


Thread t = new Thread(r);
t.setPriority(8);
t.start();
Daemon Threads

A daemon thread is a thread, that does not prevent the JVM from exiting when the
program finishes but the thread is still running.

An example for a daemon thread is the garbage collection.

You can use the setDaemon() method to change the Thread daemon properties.

When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which
typically calls the method named main of some designated class).
The Java Virtual Machine continues to execute threads until either of the following occurs:
•The exit method of class Runtime has been called and the security manager has permitted
the exit operation to take place.
•All threads that are not daemon threads have died, either by returning from the call to the
run method or by throwing an exception that propagates beyond the run method.
Join

• The non-static join() method of class Thread lets one thread "join onto the end"of
another thread. If you have a thread B that can't do its work until another thread A
has completed its work, then you want thread B to "join" thread A. This means that
thread B will not become runnable until A has finished (and entered the dead state).

Thread t = new Thread();


t.start();
t.join();

• The preceding code takes the currently running thread (if this were in the main()
method, then that would be the main thread) and joins it to the end of the thread
referenced by t. This blocks the current thread from becoming runnable until after
the thread referenced by t is no longer alive. In other words, the code t.join() means
"Join me (the current thread) to the end of t, so that t must finish before I (the
current thread) can run again."
Exercise

• 1. Write a program that runs 5 threads, each thread randomizes a number


between 1 and 10. The main thread waits for all the others to finish, calculates the
sum of the numbers which were randomized and prints that sum. You will need to
implement a Runnable class that randomizes a number and store it in a member
field. When all the threads have done, your main program can go over all the
objects and check the stored values in each object.
• 2. Modify the program in (1) so that instead of each object keeping its own score,
you will use one collection to store all the results in.
Synchronization

Its overall purpose is to only allow one thread at a time into a particular section of
code thus allowing us to protect, for example, variables or data from being
corrupted by simultaneous modifications from different threads

At its simplest level, a block of code that is marked as synchronized in Java tells the
JVM: "only let one thread in here at a time".
Synchronization

Synchronized Instance Methods

public synchronized void add(int value){


this.count += value;
}

A synchronized instance method in Java is synchronized on the instance (object)


owning the method.
Only one thread can execute inside a synchronized instance method.
If more than one instance exist, then one thread at a time can execute inside a
synchronized instance method per instance. One thread per instance.
Synchronization

Synchronized Static Methods

public static synchronized void add(int value){


count += value;
}

Synchronized static methods are synchronized on the class object of the class the
synchronized static method belongs to.
Since only one class object exists in the Java VM per class, only one thread can
execute inside a static synchronized method in the same class.
Synchronization

Synchronized Blocks in Instance Methods


public void add(int value){
synchronized(this){
this.count += value;
}
}
Mark a block of code as synchronized. This code will now execute as if it was a
synchronized method.
Synchronization

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);
}
}
}
Only one thread can execute inside any of these two methods at the same time.
Synchronization

Imagine, for example, that we have a counter that needs to be incremented at


random points in time by different threads.
Risk: two threads could simultaneously try and update the counter at the same
time, and in so doing corrupt the value of the counter (or at least, miss an
increment, because one thread reads the present value unaware that another
thread is just about to write a new, incremented value).
Solution: wrap the update code in a synchronized block, we avoid this risk:
Synchronization

public class Counter {


private int count = 0;
public void increment() {
synchronized (this) {
count++;
}
}

public int getCount() {


synchronized (this) {
return count;
}
}
}
Synchronization

Every Java object created, including every Class loaded, has an associated lock or monitor.

Putting code inside a synchronized block makes the compiler append instructions to acquire
the lock on the specified object before executing the code, and release it afterwards (either
because the code finishes normally or abnormally).

Between acquiring the lock and releasing it, a thread is said to "own" the lock.

At the point of Thread A wanting to acquire the lock, if Thread B already owns it, then
Thread A must wait for Thread B to release it.

Only methods (or blocks) can be synchronized, not variables or classes


Synchronization

Disadvantages:
- it doesn't allow concurrent read, which can potentially limit scalability
- it can only be used to control access of shared object within the same JVM. If you have
more than one JVM and need to synchronized access to a shared file system or database,
the Java synchronized keyword is not at all sufficient
- performance cost
- deadlock or starvation
Synchronization

Deadlock - describes a situation where two or more threads are blocked


forever, waiting for each other
Synchronization

Starvation describes a situation where a thread is unable to gain regular access to


shared resources and is unable to make progress. This happens when shared
resources are made unavailable for long periods by "greedy" threads
Share Resources

Producer/consumer

class Buffer {
private int number = -1;
public int get() {
return number;
}

public void put(int number) {


this.number = number;
}
}
Share Resources

class Producator extends Thread {


private Buffer buffer;
public Producator(Buffer b) {
buffer = b;
}
public void run() {
for (int i = 0; i < 10; i++) {
buffer.put(i);
System.out.println("Producatorul a pus:\t" +
i);
try {
sleep((int)(Math.random() * 100));
} catch (InterruptedException e) { }
}
}
}
Share Resources

class Consumator extends Thread {


private Buffer buffer;
public Consumator(Buffer b) {
buffer = b;
}
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = buffer.get();
System.out.println("Consumatorul a primit:\t" +

value);
}
}
}
Share Resources

Use synchronized

public synchronized void put(int number) {


// buffer blocked by producer
...
// buffer un-blocked by producer
}
public synchronized int get() {
// buffer blocked by consumer
...
// buffer un-blocked by consumer
}
Cooperation between tasks

wait - notify
class Buffer {
private int number = -1;
private boolean available = false;
public synchronized int get() {
while (!available) {
try {
wait(); // Wait for producer to put a value
} catch (InterruptedException e) {
e.printStackTrace();
}
}
available = false;
notifyAll();
return number;
}
Cooperation between tasks

public synchronized void put(int number) {


while (available) {
try {
wait(); // Wait for consumer to take value
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.number = number;
available = true;
notifyAll();
}
}
Cooperation between tasks

wait(), notify(), and notifyAll() must be called from within a synchronized context!

A thread can't invoke a wait or notify method on an object unless it owns that object's
lock.

When we enter a synchronized non-static method, we automatically acquire the lock


associated with the current instance of the class whose code we're executing (the this
instance). Acquiring a lock for an object is also known as getting the lock, or locking the
object, locking on the object, or synchronizing on the object

You might also like