0% found this document useful (0 votes)
65 views30 pages

CS0051 - M3-Locks and Liveness

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
65 views30 pages

CS0051 - M3-Locks and Liveness

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 30

MODULE 3

Locks and Liveness


Module 3A

Locks

CCS0049
CS Elective 1
Learning Objectives

Understand and apply the following locks in Java:


Reentrank Lock, Try Lock and Read-write Lock
Understand and apply the following locks in Java:
Deadlock, Abandoned Lock and Livelock

CCS0049
CS Elective 1
Reentrant Lock
• If a thread tries to lock a mutex that it's already locked, it'll enter into a waiting list for that
mutex, which results in something called a deadlock, because no other thread can unlock
that mutex.

• There may be times when a program needs to lock a mutex multiple times before unlocking it.
In that case, you should use a reentrant mutex to prevent this type of problem.
Reentrant Lock
• A reentrant mutex is a particular type of mutex that can be locked multiple times by the same
process or thread. Internally, the reentrant mutex keeps track of how many times it's been
locked by the owning thread, and it has to be unlocked an equal number of times before
another thread can lock it.

• If your program needs to lock a mutex multiple times, using a reentrant mutex may seem like
an easy way to avoid a deadlock.

• But if you don't unlock the reentrant mutex the same number of times, you can still end up
stuck
Reentrant Lock
• Many programmers like using reentrant locks because it can make things easier.

• You don't need to worry as much about what's already been locked, and they make it easier
to retrofit locks into existing code.
Reentrant Lock
Reentrant Lock
Example:
• Consider a function to increment a shared counter, and it uses a mutex to protect that
operation. If later another function is created that uses the same mutex to protect some other
section of code, and that section of code uses the increment counter function, since those
functions are nested, when executed the function will end up locking the mutex twice before
unlocking it.

• Using a regular non-reentrant lock, that would produce a deadlock, but with a reentrant mutex
this works just fine.
Reentrant Lock
Pros and Cons
• One use case where reentrant locks are really needed is when writing a recursive function,
that is, a function that calls itself.
• If the function makes a recursive call to itself from within a locked section, it will lock the mutex
multiple times as it repeats itself, and then unlock the mutex an equal number of times as it
returns and unwinds.
• Since a reentrant mutex can be used recursively like this, you'll often hear it referred to as a
recursive mutex or a recursive lock.
• Different languages use different terms, but these all basically mean the same thing.
Try Lock
• When multiple threads each have multiple tasks to perform, making those threads block and
wait every time they attempt to acquire a lock that's already taken may not be necessary or
efficient.
• Try lock or try enter is a non-blocking version of the lock or acquire method. It returns
immediately and one of two things will happen:

– If the mutex you're trying to lock is available, it will get locked, and the method with return TRUE.
– Otherwise, if the mutex is already possessed by another thread, the try lock method will immediately return
FALSE.

• That return value of true or false lets the thread know whether or not it was successful in
acquiring the lock.
Read-write Lock
• We use a locker mutex to protect a critical section of code to defend against data races,
which can occur when multiple threads are concurrently accessing the same location in
memory and at least one of those threads is writing to that location.

• That second part is key, because if we have a bunch of threads and none of them are writing,
they are all just want to read from the same location.

• It's okay to let multiple threads read the same shared value as long as no one else can
change it. They'll all safely see the same thing.
Read-write Lock
• Danger only exists when you add a thread that's writing to the mix.

• When we use a basic lock or a mutex to protect the shared resource, we limit access so that
only one of the threads can use it at a time, regardless of whether that thread is reading, or
writing, or both.

• That works but it's not necessarily the most efficient way to do things, especially when there
are lots of threads that only need to read. This is where reader-writer locks can be useful.
Read-write Lock
• A reader-writer lock or shared mutex can be locked in one of two ways.

• It can be locked in a shared read mode that allows multiple threads that only need to read
simultaneously to lock it, or it can be locked in an exclusive write mode that limits access to
only one thread at a time, allowing that thread to safely write to the shared resource.

• Since only one thread can have the write lock at a time, all other threads wanting to read or
write will have to wait until the lock becomes available again.
Read-write Lock
• In certain scenarios, read-write locks can improve a program's performance versus using a
standard mutex. But they are more complicated to implement and they typically use more
resources to keep track of the number of readers.

• There can be language dependent differences in how they're implemented that affect
performance.
Read-write Lock
Rule of thumb
• Use a shared reader-writer lock when you have a lot more threads that will be reading from
the shared data than the number of threads that will be writing to it, such as certain types of
database applications.

• If the majority of your threads are writing, then there's not much, if any, advantage to using a
read-write lock.
Module 3B

Liveness

CCS0049
CS Elective 1
Learning Objectives

Understand the issue of starvation and how it can be overcome

CCS0049
CS Elective 1
Deadlock
Dining Philosophers Problem

A classic example that's used to


illustrate synchronization issues when
multiple threads are competing for
multiple locks is the dining philosophers
problem.
Deadlock
Dining Philosophers Problem

The Dining Philosopher Problem states that K philosophers seated around a circular table with
one chopstick between each pair of philosophers. There is one chopstick between each
philosopher. A philosopher may eat if he can pickup the two chopsticks adjacent to him. One
chopstick may be picked up by any one of its adjacent followers but not both.
Deadlock
Dining Philosophers Problem
• Philosophers will continue to alternate between eating and thinking, but since they're
operating as concurrent threads, none of them knows when anyone wants to eat or think, and
that can lead to problems.

• If a philosopher gets hungry again and picks up the chopstick closest to him and another one
also gets hungry and picks up the close chopstick, they’ve come to an impasse.

• They're both stuck waiting on the other thread to release the other lock to make progress.
Deadlock
• Each member of a group is waiting for some other member to take action, and as a result,
neither member is able to make progress.

• Avoiding deadlock is common challenge in concurrent programs that use mutual exclusion
mechanisms to protect critical sections of code.

• We want our program to be free from deadlock to guarantee liveness, which is a set of
properties that require concurrent programs to make progress.

• Some processes or threads may have to take turns in a critical section, but a well-written
program with liveness guarantees that all processes will eventually make progress.
Deadlock
Deadlock
• Imagine something like a banking application with a set of bank accounts where each one has
its own mutex to ensure that only one thread will be withdrawing from or depositing funds to
that account at time.

• To transfer funds between two accounts, a thread needs to acquire the locks for both the
sender and the receiver since it would be modifying the value of both accounts.

• If there are multiple threads concurrently making transfer between the accounts, then there's
a real chance that they could end up competing for the same locks and run into this sort of
deadlock scenario.
Abandoned Lock
• If one thread or process acquires a lock, and then terminates because of some unexpected
reason, it may not automatically release the lock before it disappears.

• That leaves other tasks stuck waiting for a lock that will never be released.
Starvation
• The operating system decides when each of the threads gets scheduled to execute, and
depending on the timing of that, it can lead to problems.

• If a thread releases its lock on the critical section, but another thread doesn't get a chance to
acquire them before it takes them again, then the other will be stuck waiting again.

• If that happens occasionally, it's probably not a big deal. But if it happens regularly, then other
threads are going to starve.
Starvation
• Starvation occurs when a thread is unable to gain access to a necessary resource, and is
therefore unable to make progress.

• If another greedy thread is frequently holding a lock on the shared resource, then the starved
thread won't get a chance to execute.

• In a simple with two equal threads competing for execution time, starvation probably isn't a
concern. Both of threads should get plenty of chances to process. However, if two threads are
given different priorities, then that may not be the case.
Starvation
Causes of Starvation
• How different thread priorities get treated will depend on the operating system. But, generally,
higher priority threads will be scheduled to execute more often and that can leave low
priority threads feeling “hungry”.

• Another thing that can lead to starvation is having too many concurrent threads.
Livelock
• A livelock looks similar to a deadlock in the sense that two threads are blocking each other
from making progress, but the difference is that the threads in a livelock are actively trying to
resolve the problem.

• A livelock can occur when two or more threads are designed to respond to the actions of
each other.

• Both threads are busy doing something, but the combination of their efforts prevent them from
actually making progress and accomplishing anything useful. The program will never reach
the end.
Livelock
• The ironic thing about livelocks is that they're often caused by algorithms that are
intended to detect and recover from deadlock.

• If one or more processor thread takes action to resolve the deadlock, then those threads can
end up being overly polite and stuck in a livelock.

• To avoid that, ensure that only one process takes action chosen by priority or some
other mechanism, like random selection.
References
• Kirk, D.(2016). Programming Massively Parallel Processors: A Hands-On Approach. USA:
Morgan Kaufmann
• Balaji, P.(2015). Programming Models for Parallel Computing (Scientific and Engineering
Computation). Massachusetts: The MIT Press
• Barlas, G(2015). Multicore and GPU Programming (An Integrated Approach). USA: Morgan
Kaufmann
• Stone, B. 2019, Parallel and Concurrent Programming with Java 1, LinkedIn Learning, viewed
31 March 2020, <https://www.linkedin.com/learning/parallel-and-concurrent-programming-
with-java-1>.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy