Java ReentrantLock (Part 3) - Vanderbilt Universityschmidt/cs891s/2018-PDFs/L2-Java...10 •...
Transcript of Java ReentrantLock (Part 3) - Vanderbilt Universityschmidt/cs891s/2018-PDFs/L2-Java...10 •...
Java ReentrantLock(Part 3)
Douglas C. [email protected]
www.dre.vanderbilt.edu/~schmidt
Institute for Software Integrated Systems
Vanderbilt University Nashville, Tennessee, USA
2
Learning Objectives in this Part of the Lesson• Understand how the concept of mutual
exclusion in concurrent programs• Recognize how Java ReentrantLock
provides mutual exclusion to concurrent programs
• Know how to use ReentrantLockin Java programs
3
Using ReentrantLock in Java
4
• ArrayBlockingQueue is a blocking bounded FIFO queue
See docs.oracle.com/javase/8/docs/api/java/util/concurrent/ArrayBlockingQueue.html
Using ReentrantLock in Javapublic class ArrayBlockingQueue<E>
extends AbstractQueue<E>implements BlockingQueue<E>,
java.io.Serializable {
5
• ArrayBlockingQueue is a blocking bounded FIFO queue
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
...
Using ReentrantLock in Java
See docs.oracle.com/javase/8/docs/api/java/util/AbstractQueue.html
6
• ArrayBlockingQueue is a blocking bounded FIFO queue
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
...
Using ReentrantLock in Java
See docs.oracle.com/javase/8/docs/api/java/util/concurrent/BlockingQueue.html
7
• ArrayBlockingQueue is a blocking bounded FIFO queue
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
...
Using ReentrantLock in Java
We’ll consider both the interface & implementation of ArrayBlockingQueue
8
• ArrayBlockingQueue is a blocking bounded FIFO queue
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
...// Main lock guarding all accessfinal ReentrantLock lock;...// The queued itemsfinal Object[] items;
// items indices for next take // or put calls int takeIndex;int putIndex;
// Number of elements in the queueint count;
Using ReentrantLock in Java
ReentrantLock used in lieu of Java’s built-in monitor
objects due to their limitations
See www.dre.vanderbilt.edu/~schmidt/C++2java.html#concurrency
9
• ArrayBlockingQueue is a blocking bounded FIFO queue
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
...// Main lock guarding all accessfinal ReentrantLock lock;...// The queued itemsfinal Object[] items;
// items indices for next take // or put calls int takeIndex;int putIndex;
// Number of elements in the queueint count;
Using ReentrantLock in Java
Object state that’s being protected by the lock
10
• ArrayBlockingQueue is a blocking bounded FIFO queue
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
...// Main lock guarding all accessfinal ReentrantLock lock;...// The queued itemsfinal Object[] items;
// items indices for next take // or put calls int takeIndex;int putIndex;
// Number of elements in the queueint count;
See docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Lock.html
Using ReentrantLock in Java
Fields needn’t be defined as volatile since ReentrantLockhandles all of the atomicity, visibility, & ordering issues
11
T1
unlocked(holdCount = 0)
ArrayBlockingQueue
ArrayBlockingQueue<String> q = new ArrayBlockingQueue<String>(10);
...// Called by thread T1String s = q.take();...
Critical Section
• ArrayBlockingQueue is a blocking bounded FIFO queue
Using ReentrantLock in Java
Thread T1 acquires the lock & enters the critical section
12
unlocked(holdCount = 0)
ArrayBlockingQueue
Critical Section
locked(holdCount = 1)
lock()
• ArrayBlockingQueue is a blocking bounded FIFO queue
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
...public E take() ... {final ReentrantLock lock = this.lock;
lock.lockInterruptibly();...
Using ReentrantLock in Java
The lock’s hold count is incremented by 1
T1
13
...// Called by thread T2String s = q.take();...
Using ReentrantLock in Java
ArrayBlockingQueue
• ArrayBlockingQueue is a blocking bounded FIFO queue
unlocked(holdCount = 0)
Critical Section
locked(holdCount = 1)
T2
A call to take() from thread T2 will block until
thread T1 is finished
T1
14
unlocked(holdCount = 0)
ArrayBlockingQueue
Critical Section
locked(holdCount = 1)
T2
unlock()
• ArrayBlockingQueue is a blocking bounded FIFO queue
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
...public E take() ... {final ReentrantLock lock = this.lock;
lock.lockInterruptibly();try { ... } finally { lock.unlock();
}...
Using ReentrantLock in Java
When thread T1 finishes in take() it unlocks the lock
T1
15
unlocked(holdCount = 0)
ArrayBlockingQueue
Critical Section
locked(holdCount = 1)
T2
• ArrayBlockingQueue is a blocking bounded FIFO queue
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
...public E take() ... {final ReentrantLock lock = this.lock;
lock.lockInterruptibly();try { ... } finally { lock.unlock();
}...
Using ReentrantLock in Java
unlock()
At this point holdCountreverts back to 0
T1
16
unlocked(holdCount = 0)
ArrayBlockingQueue
Critical Section
locked(holdCount = 1)
• ArrayBlockingQueue is a blocking bounded FIFO queue
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
...public E take() ... {final ReentrantLock lock = this.lock;
lock.lockInterruptibly();try { ... } finally { lock.unlock();
}...
Using ReentrantLock in Java
See tutorials.jenkov.com/java-concurrency/locks.html#finally
Ensure lock is always released when T1 exits
the critical section
unlock()
T1
17
unlocked(holdCount = 0)
ArrayBlockingQueue
Critical Section
locked(holdCount = 1)
T2
lock()
• ArrayBlockingQueue is a blocking bounded FIFO queue
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
...public E take() ... {final ReentrantLock lock = this.lock;
lock.lockInterruptibly();...
Using ReentrantLock in Java
Thread T2 can now enter the critical section of take() & start running
18
• ArrayBlockingQueue needs touse more than ReentrantLockto implement its semantics
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
...public E take() ... {final ReentrantLock lock = this.lock;
lock.lockInterruptibly();try {while (count == 0)notEmpty.await();
return extract();} finally {lock.unlock();
}}
Upcoming lesson on “Java ConditionObject” shows more on ArrayBlockingQueue
Critical Section
ArrayBlockingQueue
T1
lock
notEmptynotFull
Using ReentrantLock in Java
A Java ConditionObject is used to coordinate multiple threads
19
• ArrayBlockingQueue needs touse more than ReentrantLockto implement its semantics
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
...public E take() ... {final ReentrantLock lock = this.lock;
lock.lockInterruptibly();try {while (count == 0)notEmpty.await();
return extract();} finally {lock.unlock();
}}Critical
Section
ArrayBlockingQueue T1
lock
notEmptynotFull
See en.wikipedia.org/wiki/Guarded_suspension & www.dre.vanderbilt.edu/~schmidt/PDF/monitor.pdf
Using ReentrantLock in Java
These mechanisms implement Guarded Suspension & Monitor Object patterns
20
End of Java ReentrantLock(Part 3)
Java ReentrantLock(Part 4)
Douglas C. [email protected]
www.dre.vanderbilt.edu/~schmidt
Institute for Software Integrated Systems
Vanderbilt University Nashville, Tennessee, USA
2
Learning Objectives in this Part of the Lesson• Understand how the concept of mutual
exclusion in concurrent programs• Recognize how Java ReentrantLock
provides mutual exclusion to concurrent programs
• Know how to use ReentrantLockin Java programs
• Appreciate Java ReentrantLockusage considerations
3
ReentrantLock UsageConsiderations
4
• ReentrantLock must be used via a “fully bracketed” protocol
void someMethod() {ReentrantLock lock = this.lock;
lock.lock();try { ... } finally { lock.unlock();
}}
ReentrantLock Usage Considerations
The thread that acquires the lock must be the one to release it
5
• ReentrantLock must be used via a “fully bracketed” protocol• This design is known as
the “Scoped Locking” pattern
ReentrantLock Usage Considerations
See www.dre.vanderbilt.edu/~schmidt/PDF/locking-patterns.pdf
void someMethod() {ReentrantLock lock = this.lock;
lock.lock();try { ... } finally { lock.unlock();
}}
The finally clause ensures that the lock is released on all paths out the try clause
6
• ReentrantLock must be used via a “fully bracketed” protocol• This design is known as
the “Scoped Locking” pattern• Implemented implicitly via
Java synchronized methods & statements
ReentrantLock Usage Considerationsvoid someMethod() {synchronized (this) {...
}}
See lesson on “Java Built-in Monitor Object”
7
• ReentrantLock must be used via a “fully bracketed” protocol• This design is known as
the “Scoped Locking” pattern• Implemented implicitly via
Java synchronized methods & statements
• This pattern is commonly usedin C++ (& C#) via constructors & destructors
void write_to_file(std::ofstream &file,const std::string &msg)
{static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex);
file << msg << std::endl;}
ReentrantLock Usage Considerations
See en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
8
• ReentrantLock supports “recursive mutex” semantics where a lock may be acquired multiple times by the same thread, without causing self-deadlock
ReentrantLock Usage Considerations
See en.wikipedia.org/wiki/Reentrant_mutex
9See github.com/douglascraigschmidt/LiveLessons/tree/master/Java8/ex24
• ReentrantLock semantics are useful for frameworks that hold locks during callbacks to user code
cDTimer: CountDownTimer
start()
onTick()
onFinish()
timerHandler
run()
cancel()
mLock.lock();try {mCancelled = true;mSchedExeSvc
.shutdownNow();} finally {mLock.unlock();
}mLock.lock();try {
...onTick(millisLeft);...
} finally {mLock.unlock();
}
if (...)cancel();
onTick() is called with the mLock held
mLock
ReentrantLock Usage Considerations
10See github.com/douglascraigschmidt/LiveLessons/tree/master/Java8/ex24
• ReentrantLock semantics are useful for frameworks that hold locks during callbacks to user code
cDTimer: CountDownTimer
start()
onTick()
onFinish()
timerHandler
run()
cancel()
mLock.lock();try {mCancelled = true;mSchedExeSvc
.shutdownNow();} finally {mLock.unlock();
}mLock.lock();try {
...onTick(millisLeft);...
} finally {mLock.unlock();
}
if (...)cancel();
onTick() can call cancel
mLock
ReentrantLock Usage Considerations
11See github.com/douglascraigschmidt/LiveLessons/tree/master/Java8/ex24
• ReentrantLock semantics are useful for frameworks that hold locks during callbacks to user code
cDTimer: CountDownTimer
start()
onTick()
onFinish()
timerHandler
run()
cancel()
mLock.lock();try {mCancelled = true;mSchedExeSvc
.shutdownNow();} finally {mLock.unlock();
}mLock.lock();try {
...onTick(millisLeft);...
} finally {mLock.unlock();
}
if (...)cancel();
cancel() also acquires mLock, so it must be recursive or self-deadlock will occur
mLock
ReentrantLock Usage Considerations
12
• ReentrantLocks can be tedious & error-prone to program due to common traps & pitfalls
ReentrantLock Usage Considerations
13
• ReentrantLocks can be tedious & error-prone to program due to common traps & pitfalls, e.g.• Holding a lock for a long
time without needing it
ReentrantLock Usage Considerationsvoid someMethod() {ReentrantLock lock = this.lock;
lock.lock();try { for (;;) {// Do something that // doesn’t involve lock
} } finally { lock.unlock();
}}
14
• ReentrantLocks can be tedious & error-prone to program due to common traps & pitfalls, e.g.• Holding a lock for a long
time without needing it• Acquiring a lock &
forgetting to release it
ReentrantLock Usage Considerationsvoid someMethod() {ReentrantLock lock = this.lock;
lock.lock();... // Critical sectionreturn;
}
15
• ReentrantLocks can be tedious & error-prone to program due to common traps & pitfalls, e.g.• Holding a lock for a long
time without needing it• Acquiring a lock &
forgetting to release it• Releasing a lock that was
never acquired• or has already been
released
ReentrantLock Usage Considerationsvoid someMethod() {ReentrantLock lock = this.lock;
// lock.lock();try { ... // Critical section
} finally { lock.unlock();
}}
16
• ReentrantLocks can be tedious & error-prone to program due to common traps & pitfalls, e.g.• Holding a lock for a long
time without needing it• Acquiring a lock &
forgetting to release it• Releasing a lock that was
never acquired• Accessing a resource without
acquiring a lock for it first • or after releasing it
Compare with lesson on “Java Built-in Monitor Objects”
ReentrantLock Usage Considerationsvoid someMethod() {ReentrantLock lock = this.lock;
// lock.lock();try { ... // Critical section
} finally { // lock.unlock();
}}
17
End of Java ReentrantLock(Part 4)