Wait here, Alphonse, for my signal
Download
Report
Transcript Wait here, Alphonse, for my signal
Counting Semaphore
Implementation in Java
CS598 – Concurrent Programming
Kasturi Kallakuri
Cindy Mayo
21 August 2002
“Alfonse, Wait Here
for My Signal!”
Author: Stephen J. Hartley
Original presentation:
ACM-SIGCSE March 1999
New Orleans, Louisiana
1 Java is Multithreaded
The Java Virtual Machine can support
many threads of execution at a time
• A thread is a separate flow of control
within a program
• Each thread has its own local variables
and lifetime
1 Java is Multithreaded
• All threads within a program share the
same address space
• Thread is represented by the Java
Thread class
• Threads can be synchronized with other
threads in same process
1.1 A Multithreaded Program
P
R
O
G
R
A
M
Threads
HotJava Web Browser is an example
of a multithread Java application
1.2 Thread Creation
Implement the Runnable Interface
Subclass (extend) the Thread class
1.2.1 Implementing Runnable
class HelloRunnable implements Runnable{
public void run()
{ System.out.println(“Hello World!”);}
}
HelloRunnable hr = new HelloRunnable();
Thread hThread = new Thread(hr);
hThread.start()
1.2.2 Subclassing Thread
class HelloThread extends Thread{
public void run(
{ System.out.println(“HelloWorld!”); }
}
HelloThread hThread = new HelloThread();
hThread.start()
1.3 Life Cycle of a Thread
born
start()
notify()
notifyAll()
ready
quantum expiration
“yield” interrupt
dispatch
running
wait()
sleep()
waiting
I/O completion
sleeping
sleep interval expires
complete
dead
issue I/O
request
blocked
1.4 Thread Priorities
By default, all threads have equal priority
setPriority() can be used to change the
default priority
Java implements MIN_PRIORITY (0) and
MAX_PRIORITY (10)
Microsoft’s JVM utilizes time slicing
Solaris’ JVM does not
2 Object Locks
Every Java Object has a lock, which is
implemented as a binary semaphore
Java supports mutual exclusion through
the synchronized keyword
Object locks may be class locks or class
instance locks.
2 Object Locks
Java does not provide a way to perform
separate locking and unlocking functions
lock() and unlock() are implicitly
performed by high-level constructs that
arrange ways to pair such actions correctly
The JVM provides separate monitorenter
and monitorexit instructions that
implement the lock and unlock actions
2.1 Mutual Exclusion
Synchronized blocks
Synchronized methods
2.1.1 Synchronized block
Acquires the object lock before executing
the body of the synchronized block
After execution of the block, unlocks the
lock
Syntax:
Object obj = new Object();
synchronized (obj)
{
// critical section
}
2.1.1 Synchronized block
class Update {
private int data = 0;
public void increment(){
synchronized(this) { //lock the
object
data++;
}
}//end of increment
} //end of class
2.1.2 Synchronized method
Automatically performs lock action when
invoked
After execution of the method’sbody,unlock
action is automatically performed on same
lock
Syntax:
Object obj = new obj();//methods use
synchronized type method(…){
//body of method
}
2.1.2 Synchronized method
bump() uses an instance lock (this)
classBump() uses the class lock
class Test {
int count;
static int classCount;
synchronized void bump()
{ count++; }
static synchronized void classBump()
{ classCount++; }
}
2.2 ConditionalSynchronization
Waiting process should leave the
semaphore but not exit the method
Leaving the object instance releases the
lock
Instances need back porch where waiters
leave and not exit the instance. [wait set]
There is only one wait set per class
instance
2.2 Conditional Synchronization
Object method wait() blocks calling thread
in wait set.
Object method notify() unblocks a thread
if any in the wait set
Object method notifyAll() unblocks all
threads in the wait set.
2.3 Deadlock
Java does not prevent, nor require
detection of deadlock conditions
Programs where threads hold locks on
multiple objects should use conventional
techniques for avoiding deadlock
• Create higher level locking primitives
that cannot deadlock, if necessary
2.3.1 Mutex behaviour
Mutex unblocking is arbitary
Thread priority is ignored
Thread starvation is possible
Mutex locking is re-entrant
A thread can relock any mutex it holds
2.3.2 Interference
Interference with conditional
synchronization
Unblocked thread may not immediately
regain the mutex
The signaled condition may no longer be
true when it does
2.4 Points to Remember
The acquisition and release of locks is
done automatically and atomically by the
Java Virtual Machine (JVM), which
guarantees that:
• Race conditions cannot occur
• Data integrity is ensured
Mutex behaviour needs to be worked
around
3 Java Monitors
Monitors are a data abstract mechanism
Monitors encapsulate data without
external access and references outside of
the monitor are blocked
Monitors contain variables that store the
object’s state and procedures that
implement operations on the object
3 Java Monitors
Mutual exclusion is provided implicitly by
ensuring that procedures in the same
monitor are not executed concurrently
Condition synchronization is provided
explicitly by condition variables
3.1 Java Monitor Structure
class Monitor {
private .. //data fields
Monitor(..){…} //constructor
synchronized type method1(){
notifyAll();
while ( !condition )
try { wait(); }
catch(InterruptedException e){}
notifyAll();
}
Note: Monitors are compile time entities
3.2 Concurrency Control
Mutual Exclusion
Conditional Synchronization
3.2.1 Mutual Exclusion
A monitor procedure is called by an
external process
At most, one instance of monitor
procedure may be active at a time
Only one thread at a time is allowed inside
the monitor
3.2.1 Mutual Exclusion
Monitor entries are (java) synchronized
always.
Monitor procedures by definition execute
with mutual exclusion
It is up to the language and operating
system to provide mutual exclusion
3.2.2 Condition
Synchronization
A condition variable is used to delay a
process that cannot safely continue
executing until the monitor’s state satisfies
some Boolean condition
There may be many condition variables per
monitor
Monitor’s back porch
3.2.2 Condition
Synchronization
A process queries the state of a condition
variable by calling empty()
A process blocks on a condition variable by
calling wait()
Processes blocked on a condition variable
are awakened by a call to signal()
3.3 Signal and Continue
Signal and
continue
call
Entry
queue
Condition
variable
queue
monitor
free
wait()
Executing
in monitor
return
3.3 Signal and Continue
Signal and continue is non
preemptive
Condition variable release is FIFO
Relation between signaled and
signaler:
• Signaled jumps to the front of the entry
queue signaler continues
3.4 Perils of Monitors
The thread blocked longest on a monitor
synchronized method call is not
guaranteed to be next thread to acquire
the monitor lock when the monitor lock is
released
3.4 Perils of Monitors
The thread blocked the longest in a wait()
call is not guaranteed to be the one
removed from the wait set when a notify()
call is made
Typically, FIFO is used to determine who
gets control (JVM/platform specific)
3.4 Perils of Monitors
A thread waiting for the monitor lock to
execute a monitor synchronized method
might get the lock before a signaled thread
reacquires the lock even if the notify
occurred earlier than the monitor method
call
A thread should always recheck its wait
condition when signaled
3.4 Perils of Monitors
The while construct is preferred over the
if construct
while ( !condition )
wait();
notifyAll();
3.4 Perils of Monitors
Each monitor has a single, nameless
condition variable
Cannot specify which of the waiting
threads should be released by a notify()
call
May need to use notifyAll() to awaken
all threads and allow contention
3.4 Perils of Monitors
sleep(), join(), and wait() throw
InterruptedException
No exception is thrown if a thread is
interrupted while blocked
An exception is thrown by wait() if a
thread that has been notified is
interrupted while waiting to reacquire the
monitor lock
3.4 Perils of Monitors
Ignoring InterruptedException with an empty
catch block can be acceptable in a while loop
while ( !condition )
try { wait(); }
catch (InterruptedException e) {}
Must be using notifyAll()
3.4 Perils of Monitors
Cannot ignore InterruptedException when
using an if construct
if ( !condition )
try { wait(); }
catch (InterruptedException e) {}
Notification slip
3.4 Perils of Monitors
It is desirable to have the containing method
throw the exception back to the calling
function so that the calling function knows
that an exception has actually occurred
4 Counting Semaphores
V() operation
if threads blocked in P(), unblock one
else increment semaphore value
P() operation
if semaphore value is 0 block semaphore
else decrement semaphore value
4 Counting Semaphores
Safety
Liveness
Barging
Signaling
Exception propagation
4.1 First Attempt
public class CountingSemaphore
{ private int value = 0;
public CountingSemaphore(int initial)
{ if ( initial > 0 )
value = initial;
}
4.1 First Attempt
public synchronized void P()
throws InterruptedException
{ if ( value = = 0 )
wait();
value--;
}
4.1 First Attempt
public synchronized void V()
{ if (value = = 0)
notify();
value++;
}
}
// End of class CountingSemaphore
4.2 Second Attempt
public synchronized void P()
throws InterruptedException
{ while ( value = = 0 )
wait();
value--;
}
4.3 Third Attempt
public synchronized void P()
throws InterruptedException
{ value--;
if ( value < 0 )
wait();
}
4.3 Third Attempt
public synchronized void V()
{ value++;
if ( value <= 0 )
notify();
}
4.4 Fourth Attempt
public class CountingSemaphore
{ private int value = 0;
private int waitCount = 0;
public CountingSemaphore(int initial)
{ if ( initial > 0 )
value = initial;
}
4.4 Fourth Attempt
public synchronized void P() throws IE
{ if ( value = = 0 || waitCount > 0 )
{
waitCount++;
try {
wait(); }
catch(IE e)
{
notify();
throw e;
}
finally {
waitCount--; }
}
value--;
}
4.4 Fourth Attempt
public synchronized void V()
{ value++;
if ( waitCount > 0 )
notify();
}
4.5 Fifth Attempt
public class CountingSemaphore
{ private int value = 0;
private int waitCount = 0;
private boolean notified = false;
public CountingSemaphore(int initial)
{ if ( initial > 0 )
value = initial;
}
4.5 Fifth Attempt
public synchronized void P() throws IE
{ if ( value = = 0 || waitCount > 0 )
{
waitCount++;
try
{
do { wait(); }
while ( !notified ); }
// catch and finally unchanged
notified = false;
}
value--;
}
4.5 Fifth Attempt
public synchronized void V()
{ value++;
if ( waitCount > 0 )
{
notified = true;
notify();
}
}
4.6 Sixth Attempt
public class CountingSemaphore
{ private int value = 0;
private int waitCount = 0;
private int notifyCount = 0;
public CountingSemaphore(int initial)
{ if ( initial > 0 )
value = initial;
}
4.6 Sixth Attempt
public synchronized void P() throws IE
{ if ( value = = 0 || waitCount > 0 )
{
waitCount++;
try
{
do { wait(); }
while (notifyCount = = 0);}
// catch and finally unchanged
notifyCount--;
}
value--;
}
4.6 Sixth Attempt
public synchronized void V()
{ value++;
if ( waitCount > 0 )
{
notifyCount++;
notify();
}
}
4.7 Seventh Attempt
public synchronized void P() throws IE
{
if ( value < waitCount )
waitCount++;
try{
do{wait();}
while(notifyCount ==0);
}catch(InterruptedException e){
notify();
throw e;
}finally{waitCount--}
notifyCount--;
}
// if block remains unchanged
else
{
if ( notifyCount > waitCount )
notifyCount--;
}
value--;
}
4.7 Seventh Attempt
public synchronized void V()
{ value++;
if ( waitCount > notifyCount )
{
notifyCount++;
notify();
}
}
5 Conclusions
It is difficult to write correct,
synchronized, multithreaded Java
applications
Java monitors are very low-level
synchronization tools
Java needs class libraries that
provide user-friendly synchronization
tools
6 Selected References
Andrews, G.R. Concurrent
Programming: Principles and
Practice. Benjamin/Cummings, 1991.
Lea, D. Personal communications.
The Java Language Specification
Gosling,J,Joy,B,and Steele,G AddisonWesley 1996
7 Reactions
Java language
CountingSemaphore class
Conclusions
References