Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Programming
All threads are equal, but some
threads are more equal than other…
CONCURRENCY
TERMINOLOGY
AND CONCEPTS
Introduction
What is a sequential program?
◦ A single thread of control that
executes one instruction and
when it is finished execute the
next logical instruction
What is a concurrent program?
◦ A collection of autonomous
sequential threads, executing
(logically) in parallel
Concurrency is not (only)
parallelism
Introduction
Why use concurrent programming?
◦ Natural Application Structure
The world is not sequential
◦ Increased application throughput and
responsiveness
Not blocking the entire application due
to blocking IO
◦ Performance from multi-core
hardware
Parallel execution
◦ Distributed systems
1-tier, 2-tiers, etc.
Introduction
Speed me up! But what’s the
problem?
◦ Consider the following code:
Start with: x = y = 0
Thread #1:
x = 1;
j = y;
Thread #2:
y = 1;
i = x;
◦ Possible result of execution: i = j =
0
Introduction
How is this possible?
◦ Reorder of assignments by compiler
◦ Reorder of assignments by
processor
◦ Values kept in registers
◦ Values from the working memory
are not synchronized with main
memory
What?
Introduction
Butit gets even worse…
Look carefully at this one
◦ Start with some pointers p = q, p.x = 0
◦ Thread #1 Thread #2
r1 = p; r6 = p;
r2 = r1.x; r6.x = 3;
r3 = q;
r4 = r3.x;
r5 = r1.x;
◦ Surprisingly r4 might be equal to 3 and at the
same time r5 = 0!
◦ This is because compiler can replace the last line
by: r5 = r2;
H u h ??
This is called forward substitution
Three Aspects of
Synchronization
Atomicity
◦ Locking to obtain mutual exclusion
Visibility
◦ Ensuring that changes to object
fields made in one thread are seen
in other threads
Ordering
◦ Ensuring that you aren’t surprised
by the order in which statements
are executed
Atomicity
A set of operations can be
considered atomic when two
conditions are met
◦ Until the entire set of operations
completes, no other process can
know about the changes being
made (invisibility)
◦ If any of the operations fail then the
entire set of operations fails, and
the state of the system is restored
to the state it was in before any of
the operations began
Atomicity
Example
◦ Consider storing a value into the
memory
◦ If two processes will store to the
same memory location at the
same time, and the storing
operation is not atomic – unknown
value will be left in this memory
location
Atomicity
Atomicity cannot be implemented
in
user level only
A critical section is a piece of
code that accesses a shared
resource that must not be
concurrently accessed by more
than one thread of execution
A critical section is implemented
in user-level to be atomic using
locks
Atomicity in Java
Accesses and updates to the
memory cells corresponding to
fields of any type except long or
double are guaranteed to be
atomic
◦ This includes fields serving as
references
to other objects (either 32/64-bit
references)
volatile
long and double are also
guaranteed to be atomic
Visibility
Memory updates performed by
one thread are not necessarily
seen by other threads
◦ There is no time constraints for
memory synchronization
May never happen
◦ Some threads might see the
updated value while other won’t
Visibility
Every thread in the system has
(logically) its own working
memory in which it operates
◦ Including registers, CPU cache
(L1/L2), etc.
◦ Can even reside on different
machine
To share data between threads, a
main memory is used
◦ In practice, many times the
separation is logical only and
Visibility
Example
◦ Thread #1:
alive = true;
while (alive) {
…
}
◦ Thread #2:
alive = false;
◦ Thread #2:
if (finished)
System.out.println(num);
◦ May print 0
Ordering
Memory barrier is a class of
instructions which enforce an
ordering constraint on memory
operations issued before and
after the barrier instruction
Simplest barrier: full hence
◦ A full fence ensures that all load
and store operations prior to the
fence will have been committed
prior to any loads and stores
issued following the fence
Lock
A lock (aka mutex - acronym for
mutual exclusion) is a tool for
controlling access to a shared
resource by multiple threads
◦ It is the user level tool for critical
sections
Usually, a lock will provide two
atomic operations: lock and
unlock
Locking a lock will alter its state
in a way that will not permit any
Lock
The result of an attempt to lock an
already locked lock is
implementation specific
Usually, the attempter thread is
blocked until the lock is unlocked
by the original locking thread
◦ A queue is used to remember the
blocked threads awaiting for the lock
◦ Whenever the lock is unlocked, the
first blocked thread will resume and
lock the lock
This is called a fair lock, other scheduling
Lock T1 T2
Example lock.lock()
lock.lock()
foo()
◦ Thread #1:
// blocked
lock.lock(); …
foo(); lock.unlock()
lock.unlock(); // lock()
returns
◦ Thread #2:
bar()
lock.lock(); …
bar(); lock.unlock()
lock.unlock();
Is interpreted to
void myMethod() {
synchronized(this) {
// critical section code
}
}
Synchronization
And also
class MyClass { …
void static synchronized myMethod() {
// critical section code
}
}
Is interpreted to
…
void myMethod() {
synchronized(MyClass.class) {
// critical section code
}
Synchronization
How strong is this lock?
◦ As any lock, any piece of code
synchronized using the same
monitor is atomic
◦ It is reentrant lock
◦ Java also put constrains on
reordering
Synchronized blocks with the same
monitor cannot be reordered with
respect to each other
Yet, reordering is permitted for the
rest of the code
including the insertion of statements into
Synchronization
And visibility?
◦ All memory updates before a
release are visible after his
matching acquire
In other words – all updated inside a
synchronization block are visible to
any latter synchronization block of
the same monitor
You can think of a release as a point
in which working memory is
published to main memory, and an
acquire as a point in which the
working memory is synchronized
from the main memory
Volatile Fields
In terms of atomicity, visibility, and
ordering, declaring a field as volatile is
like using a little fully synchronized
class protecting only that field, as in
◦ Thread #1
synchronized(myMonitor)atomic
{
myMonitor.wait(); lock.unloc
k()
} suspend
lock.lock()
◦ Thread #2
synchronized(myMonitor) {
myMonitor.notifyAll();
}
Wait / Notify
A call to wait / notify outside a
synchronization block will result in
llegalMonitorStateException
Recall the differences between notify /
notifyAll
The call to wait can be time limited
◦ When wait returns, there is no way to
know whether it was woken up or the
timeout has reached
JVM is allowed to wake up a waiting
condition for no reason
◦ This is called a spurious wake-up
Sleep and Yield
Class Thread contains also the
sleep and yield static methods
Both have no impact on monitors,
but on scheduling only
Sleep cause the Thread not to be
scheduled for execution for a
minimum amount of time
(unless interrupted)
Yield only suggests the scheduler
to perform a context switch
Thread Life Cycle
A Thread in Java has the following life-
cycle
◦ It starts after the call to its start()
method
Calling start synchronizes with the
threads actual start, so no reorder or
visibility issues exists between the
two
◦ It ends after its run method returns
Either the method returns or throws
an exception
The end of a thread is synchronized
with any attempt to detect its
Thread Interruption
Thread can be interrupted using the
interrupt() method in the class
Thread
Interrupting a thread will cause one of
the following to that thread
◦ If thread is blocked in the invocation of a
wait, join or a sleep methods it will
throw an InterruptedException
◦ If thread is blocked on an IO operation it
might throw some interruption
exception (too long for here…)
On some OS it might also interrupt a system
call
Thread Interruption
Ifflag interruption status is set, a call
to either wait, join or sleep will throw
immediately InterruptedException
and then the status will be cleared
A call to the static interrupted()
method of the class Thread will
return the current thread interruption
status, and clear it right afterwards
Confusingly, the isInterrupted()
method of class Thread will return
the interruption status without
altering its state
Deprecated Thread
Methods
Class Thread had many design
mistakes when it was first published
For backwards compatibility those
mistakes still exists as deprecated
methods
First deprecated mechanism is stop
◦ Its purpose was to enable killing a thread
brutally
◦ This is unsafe since it breaks all monitors
that the thread is locking
◦ The right solution is a user-level flag for
thread’s liveness, that is checked
periodically by the thread
Deprecated Thread
Methods
Anotherdeprecated mechanism is
Thread.suspend() and
Thread.resume()
◦ Its purpose was to enable external thread
to decide on threads scheduling
◦ It is deprecated since it promotes
deadlocks
The suspended thread still locks its monitors
The call to resume() might get lost before the
call to suspend() and thus the thread will
never resume
◦ The right solution is to use the new
java.util.concurrent.lock.LockSupport
It contains the park() and unpark()static
Thread Groups
A thread group represents a set
of threads
Thread group is a composite, so it
can contain other thread groups
as well
A thread is allowed to access
information about its own thread
group, but not to access
information about its thread
group's parent thread group or
any other thread groups
Thread Specific Storage
(TLS)
Use static or global memory that is
localized to a Thread
◦ Normally all heap data is shared across
all threads
◦ Writing a parallel code, feeling like a
single thread
◦ No locking it needed
Classical example
◦ errno: Global storage of the last system
call result
Implementation
◦ Simply use the ThreadLocal class
Word Tearing
Java guarantees that every field and array
element is considered distinct
◦ Updates to one field or element must not interact
with reads or updates of any other field or
element
Inparticular, two threads that update
adjacent elements of a byte array
separately must not interfere or interact
even of no synchronization is defined
Some processors do not provide the ability to
write to a single byte
◦ It would be illegal to implement byte array
updates on such a processor by simply reading
an entire word, updating the appropriate byte,
and then writing the entire word back
Part III
JAVA.UTIL.CONCURRENT
“ T h e ja va . u tilco
. n cu rre n t p a cka g e
in JD K 1 . 5
is w o rth its w e ig h t in In te rn e t
p o rn ”
From studdugie on Java
Introduction
The util.concurrent package was
originally written as open source by
Doug Lea
In JDK 1.5, Sun imported it into the JDK
and since then it is widely used and
recommended
The goal of the package was described
as
◦ “…to make concurrent programs clearer,
shorter, faster, more reliable, more
scalable, easier to write, easier to read,
and easier to maintain…”
◦ And it does so pretty well!
java.util.concurrent
◦ Presentation from JavaOne 2006
Asynchronous Method Invocation
(AMI)
Invoke a method on a different thread
than the callers thread
◦ Method returns immediately
◦ Several ways to retrieve the result
Method can return a Future Object
Can tell whether the method finished its
execution
Can wait for method to finish and return its
result
Sometime can also cancel the execution
process
Method can receive a callback reference to
notify when it finishes
Can also be preregistered as a listener, thus
enabling more then
Active Object
Decouplemethod execution from
method invocation using AMI
◦ Store a queue of requests
Potentially “smart” queue (e.g. priority
queue)
Potentially Command requests
◦ Each method execution is replaced by
adding a request to the queue
◦ Active Object runs in its own thread,
taking requests from the queue and
invokes them
Response can be returned as discussed in AMI
Active Object
Pros
◦ Write the critical (synchronous) session once
(avoid race conditions)
◦ Simple interface to invoke Commands
◦ Asynchronous
◦ Easy to debug
Compared to asynchronous code
Cons
◦ Non-intuitive response method
◦ Difficult to debug
Compared to synchronous code
Implementation
◦ Use BlockingQueue
Part IV
DOUBLE CHECK
LOCKING
Singleton
Consider
the naïve Singleton
implementation
private Singleton() { … }
private Singleton() { … }
private DoubleLockMechanism() { … }
synchronized(DoubleLockMechanism.class) {
if (INSTANCE == null) {
INSTANCE = new
DoubleLockMechanism();
}
}
} ØDoesn ’ t work !
return INSTANCE ;
ØMight return unconstructed
}
instance
}
Double Check Locking –
Wrong
public static singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
Singleton inst = instance;
if (inst == null) {
synchronized(Singleton.class) {
inst = new Singleton();
}
}
instance = inst;
}
}
return instance;
}
Double Check Locking -
Wrong
private volatile boolean initialized = false;
private static Singleton instance;
public static Singleton getInstance() {
if (instance == null || !initialized) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
initialized = (instance != null);
}
return instance;
}
Double Check Locking -
Wrong
public final class FullMemoryBarrierSingleton {
private static boolean initialized = false;
private static Resource resource = null;
private static Object lock = new Object();
public static Resource getResource() {
if (!initialized) {
synchronized (lock) {
if (!initialized && resource == null) resource
= new Resource();
} synchronized (lock) {
initialized = true;
}}
return resource;
} }
JMM doesn’t permit changes so it’s write-barrier but not
read-barrier safety
Double Check Locking –
Solution 1
Remember this?
public final class Singleton {
private static final Singleton INSTANCE = new
Singleton();
private Singleton() { … }
Bill Pugh
Double Check Locking –
Solution 3
public final class DoubleLockMechanism {
Joshua Bloch
References
Wikipedia
http://java.sun.com/docs/books/jls/thir
http://gee.cs.oswego.edu/dl/cpj/jmm.h
http://www.softwaresummit.com/2003
http://docs.huihoo.com/javaone/2006/
http://developers.sun.com/learning/jav