Conucrrency in Java - Pascal-Man

advertisement
Concurrency in Java
(Shooting yourself in the foot)n
Acknowledgment
This lecture is derived almost
exclusively from
Java Concurrency in Practice
by Goetz, et. al.
http://www.javaconcurrencyinpractice.com
Additional notes from Dr. Dan
Wallach
Outline
Background
Basic Thread Safety
Concurrent Object Management
Concurrent Library Components
Task Execution and Shutdown
Next Time
Thread Pools
GUI Applications
Safety and Performance
Documentation and Testing
Adv. Topics
Explicit Locks
Custom Synchronizers
Nonblocking Synchronization
Background
Why concurrency?
Resource utilization - why wait?
Fairness - why wait?
Convenience - why wait?
First concurrency: processes
Later: threads
Benefits of Threads
Responsiveness (esp. GUIs)
Exploiting multi-processors
Simplicity of modeling
Simplified handling of
asynchronous events
Risks of Threads
Java’s “built-in” threads means
that concurrency is NOT an
advanced topic
Safety hazards (correctness)
Liveness hazards (progress)
Performance hazards
(happiness)
Threads are Everywhere
Frameworks use threads
Timer
Servlets and JSPs
RMI
Swing and AWT
You will use this one!
Starting a Java Thread
public class HelloRunnable implements Runnable {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new Thread(new HelloRunnable())).start();
}
}
public class HelloThread extends Thread {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new HelloThread()).start();
}
}
Outline
Background
Basic Thread Safety
Concurrent Object Management
Concurrent Library Components
Task Execution and Shutdown
Synchronization
Given a mutable variable v
If multiple threads access v
AND if one can modify v
ALL must synchronize access
This includes read access
Never attempt to ignore this
If sync is broken, so is the
code (even if it passes tests)
Concurrent Correctness
Fixing a broken “shared” var
Don’t Share! Or
Make the var immutable! Or
Synchronize access
Better yet, design it right
Encapsulation
Immutability
Clear specifications of invariants
What is “Thread-Safe”?
Definitions are vague and vary
Problem: what is correctness?
Thread Safe:
“Correct” in multi-threaded env
OR, no more broken in a multthreaded environment then in a singlethreaded one
Thread-safe classes encapsulate all
synchronization
Example:
Unsafe Counter
@NoThreadSafe
public class UnsafeSequence{
private int value;
public int getNext() {
return value++;
}
}
What’s Wrong with That?
Invariant:
getNext must return a sequence
Unlucky execution timing:
Value->9
Value->9
9+1->10
Value=10
9+1->10
Value=10
Race Conditions
The problem is a race condition
Def: r.c. occurs when the
correctness of a computation
depends on the relative timing
of multiple threads by the
runtime.
Often confused for data race
Achieving Safety
Go stateless!
Use atomic operations
Use locking
Using Atomics!
(It’s not a treaty violation)
@ThreadSafe
public class UnsafeSequence{
private final
AtomicLong value = new AtomicLong(0);
public long getNext() {
return value.incrementAndGet();
}
}
Locking
Solved one mutable variable by
making the var atomic
What if we have 2? Can we
just make them both atomic?
NOT if they are dependent
We have to lock any combined
operations
Example
Suppose we have a dictionary
that stores the last (key,value)
pair
Setting the (k,v) pair must be
locked. It is not enough to use
an atomic var for k, and
another one for v
Intrinsic Locks
Java provides built-in locks
Each object is a lock, so is
each class
Marked by synchronized
Enforces
Atomicity
Memory visibility (more later)
Reentrancy
Requesting a lock held by
another causes a block
Intrinsic locks are reentrant
A thread that owns a lock, that
requests the same lock, will
succeed (good for inheritance!)
Example Locking
@ThreadSafe
Public class LockSafe<K,V> {
@GuardedBy(“this”) private K key;
@GuardedBy(“this”) private V value;
public synchronized setKvPair(K k, V v) {
this.key = k;
this.value = v;
}
}
Locks: “Guarded By”
“For each mutable state variable
that may be accessed by more
than one thread, all accesses
to that variable must be
performed with the same lock
held. In this case, we say that
the variable is guarded by that
lock” (JCP 28)
Locks: Documentation
“Every shared, mutable variable
should be guarded by exactly
one lock. Make it clear to
maintainers which lock that is”
- (JCP p28)
Locks: Across Invariants
“For every invariant that involves
more that one variable, all the
variables involved in that
invariant must be guarded by
the same lock” - (JCP p29)
Critical Concept
Any mutable state that can be
concurrently accessed, must
be synchronized EVERYWHERE
else in the program!
EXAMPLE: TimerTask (JCP
p29)
Why Not Lock Everything?
Even if every method were
synchronized, it doesn’t solve
actions that combine those
methods! (example: vector)
Also, possible liveness and/or
performance problems
Poor concurrency example
(JCP p30)
Lock Advice
“Avoid holding locks during
lengthy computations or
operations at risk of not
completing quickly such as
network or console I/O” - (JCP
p32)
More Syntax
Other use of synchronized
public somemethod() {
synchronized(this) {
// do stuff
}
}
Outline
 Background
 Basic Thread Safety
 Concurrent Object Management
 Concurrent Library Components
 Task Execution and Shutdown
Sharing Objects
Memory Visibility
Shared object modification
should be visible to other
threads
Without synchronization, this
may not happen
More about Visibility
“In general, there is no
guarantee that the reading
thread will see a value written
by another thread on a timely
basis, or even at all” - (JCP
p33)
Public class NoVisibility {
private static boolean ready;
private static int number;
private static class ReaderThread extends Thread {
public void run() {
while (!ready) Thread.yield()
System.out.println(number)
}
}
public static void main(String[] args) {
new ReaderThread().start();
number = 42;
ready = true;
}
}
NoVisibility :(
NoVisibility could loop forever!
NoVisibility could write 0
through reordering
Other badness and sadness
Don’t do this!
Synchronization Helps
NoVisibility demonstrates stale
data
Synchronization must be used
on all shared variables, even
just for reads
Enforces memory visibility
A Word about 64bit
When a thread reads a var w/o
synchronization, at worst it is
stale, but not random
Out-of-thin-air safety
One exception: 64 bit numbers
(double and long)
Read/write can be 2 32 bit
operations!
Volatile Variables
Can declare a java var volatile
 Volatile ensures visibility, but not
locking.
Volatile use can be fragile. Many
times, you shouldn’t use it
Good uses of volatile
Ensure visibility of their own state
That of the object they refer to
Or indicate that an important event has
occurred (e.g. shutdown)
Use Volatile ONLY when
Writes to the var do not depend on
its current value or are only written
by one thread
The var does not participate with
invariants with other state vars
Locking is not required for any
other reason when the var is being
accessed
Publication and Escape
Publishing an object - makes it
available outside current scope
Many times, objects should not be
published at all
Breaks encapsulation
Not fully constructed (!)
Object published when it should
not have been is said to have
escaped!
Publication Methods
Public static fields
Chained publication
Special Case: published inner
class
Passing to an alien method
How Escape Happens
All linked to poor design
Semantic escapes:
Return ref instead of copy
Inadvertent chained publication
Syntactic escapes:
Escaped this during construction
“Object not properly constructed”
This Escape Example
Public class ThisEscape {
// JCP p41
public ThisEscape(Eventsource source) {
source.registerListener(
new EventListener() {
public void onEvent(Event e) {
doSomething(e);
}
});
}
}
Preventing Escape
Thread Confinement
Immutability
Safe Publication
Thread Confinement
Keep mutable vars confined to
a single thread
Ad-hoc = enforced by impl.
Stack Confinement
Local vars
Violated by publication
ThreadLocal
Per thread value holding object
Immutable Objects
Always thread safe
Can’t escape after construction
Definition
It’s state cannot be modified
after construction
All fields are final*
Properly constructed
Can have mutable variables
Final Fields
Can’t be modified
AND, have special semantics in
the Java Memory model
(initialization safety)
Make all fields final by default
“Mostly Immutable” is better
than mutable
Using Volatile Again!
Volatile can be used to publish
immutable objects
Still not locked, but can be
safe depending on semantics
Example - JCP p49-50
Improper Publication
If synchronization is not used
to publish a mutable object,
the object is not properly
published
Without proper publication,
there are serious problems
with stale data
Safe Publication
Object reference and object state
must become visible at the same
time!
Idioms:
Initializing from static initializer
Storing ref in volatile or
AtomicReference
Storing ref in final field of properly
constructed object
Storing ref in a lock-guarded field
Good News!
Java Collections safe publication
examples:
Key or value in Hashtable,
synchronizedMap, or ConcurrentMap
Insert in Vector,
CopyOnWriteArrayList,
CopyOnWriteArraySet,
synchronizedList, or synchronizedSet
Insert in BlockingQueue or
ConcurrentLinkedQueue
More Good News
Effectively Immutable Objects
Objects that are used as if
they were immutable
Example: Date
Modification Vs Visibility
Safe publication ensures
visibility
Synchronization is required if
the object can be modified
post-publication
Rules of Engagement
Thread-Confined: no sharing
Shared read-only:
(effectively) immutable objects
Shared thread-safe: internal
synchronization
Guarded: only access with
the associated guard
Designing Thread-Safe
Classes
Identify the variables that form
the object’s state
Identify the invariants that
contain the state variables
Establish a policy for managing
concurrent access to the
object’s state
Java Monitor Pattern
Principle of instance
confinement
Encapsulate all mutable state
and guard it with intrinsic lock
Good coarse-grained locking
Delegating Thread
Safety
Safety can often be delgated
to an internally-used threadsafe object
Collections are especially
useful
Only works for 1 state var, or
multiple independent vars
Extending Thread-safe
Classes
BE CAREFUL! Extending means that
the synchronization policy is
distributed over multiple separately
maintained classes!
Inheritance requires the base class
to publish enough state
Wrappers require that the same
lock is used
Composition is often less fragile
Extension Examples:
Inheritance (JCP p72)
Wrapping (JCP p72-73)
Composition (JCP p74)
Outline
 Background
 Basic Thread Safety
 Concurrent Object Management
 Concurrent Library Components
 Task Execution and Shutdown
Synchronized Collections
Are thread safe, but
Composite actions require
additional locking for semantic
correctness
Careless use of locking leads to
poor performance (think
iteration)
Iterators
The synchronized collections
provide iterators
These iterators are fail-fast
Throws
ConcurrentModificationException
Concurrent Collections
Designed for concurrent access
Improved performance with a
few minor trade-offs:
Your assignment: go look up
the docs on
ConcurrentHashMap and
CopyOnWriteArrayList and try
them out!
Blocking Queues
Support the producerconsumer pattern
Can be bounded or unbounded
Serial thread confinement
Try it out!
Blocking
Threads may block
Wait
Wait
Wait
Wait
for
for
for
for
I/O
Lock
Thread.sleep
computation
put and get from
BlockingQueue block
Interruption
interrupt() method
Request a blocking thread stop
A blocking method should
throw an InterruptedException
If you catch this
Propagate the exception OR
Restore the interrupt
Example:
Try{
someBlockingMethod();
} catch (InterruptedException) {
Thread.currentThread().interrupt();
}
Synchronizers
Latches: one-time trigger
e.g. CountDownLatch
FutureTask: long-running result
Has a get() method
Returns value if done
Blocks until it is done if not
More Synchronizers
Semaphore: virtual permits
Now a library class!
Barriers: wait for enough threads
Useful for threaded stepping
Exchanger 2-party barrier
Outline
Background
Basic Thread Safety
Concurrent Object Management
Concurrent Library Components
Task Execution and Shutdown
Advanced Execution
The Executor Framework
Decouples task submission and
task execution
Simple interface:
void execute(Runnable command)
Easiest way to implement a
producer-consumer design in an
application
Execution Policies
Answers
In what thread will tasks be run
What order should they be run
How many concurrently?
How many queued?
“Victim” selection
Setup and Teardown
Policy Examples
Single-thread (JCP p119)
1-thread-per-client (JCP p118)
Thread Pools: some built in
newFixedThreadPool
newCachedThreadPool
newSingleThreadExecutor
newScheduleThreadPool
Cancellation & Shutdown
Reasons for cancellation
User-requested
Time-limited
Events
Errors
Shutdown
Cancellation Policy
“how” “when” and “what” of
cancellation
Not enforced by Java, but
generally, use interruption for
cancellation
E.g. have a cancel() method
call the interrupt
Interruption Policy
Determines how a thread
interprets an interruption
request
Tasks are “guests” in a thread
Preserve interruption status
Only call interrupt, when you
know the interruption policy
Detecting Interrupts
Polling the thread’s interrupted
status
Detecting an
InterruptedException
Non-interruptible Blocks
Synchronous socket I/O:
Close the socket
Asynchronous I/O with Selector:
Call wakeup
Lock Acquisition:
Use the explicit Lock class
Has a lockInterruptibly() method
Stopping a ThreadBased Service
 ExceutorService extends Executor
Provides “Lifecycle” options
shutdown()
awaitTermination(timeout, unit)
Any thread based service
should provide similar methods
Waiting for Threads
In the “raw” case, use join()
A number of Java library
classes have advanced “waits”
like ExecutionService’s
awaitTermination()
Obviously, if you implement
your own ExecutionService,
you’ll have to use the raw stuff
Download