Въведение в java.util.concurrent
description
Transcript of Въведение в java.util.concurrent
Михаил Стойнов &
Красимир Семерджиев
Hardware evolution - free lunch is over Java concurrency before Java SE 5 Atomic operations Java.util.concurrent overview Java SE5 vs. Java SE6
?!?!?
Moore’s law for CPU clock speed is over
Intel talks about 100x core CPU
Next major revolution in software - concurrency
Majority of software is not ready for that
“There ain’t no such thing as a free lunch.” —R. A. Heinlein,
Availability – faster response time, maximize throughput, access DBs/backends
GUIs– autonomous objects, animation, concurrent events
Parallelism – utilizing CPUs, overlapping I/O, multiple clients
Protection – isolating different activities,
Hard to implement!
Higher resource usage
Need to start decomposing tasks
Deal with safety and isolation
System(VM) = Objects + Activities(Threads) Activities touch/change objects’ state Every called method should eventually
execute as soon as possible Liveness and safety not enforced by
compilers
Java Virtual Machine
Every object has a lock Every Class has a lock (being an Object) Acquiring locks works through
“synchronized” blocks/methods Can “temporarily release” the lock by
calling wait() or wait(ms) Can wake up waiting threads by calling
notify() or notifyAll() Synchronized blocks control atomicity
Object
method
lock
method
method
Objects represent a state and procedures over it
Every Thread represents the state of a single activity touching different objects
Synchronized block ensures thread-local cache flush/reload
Volatile keyword ensures per-variable cache flush/reload
Stateless objects – comprise mainly of methods
Immutable objects – state never changes after creation
Atomic objects – legal-to-legal state changes only
Locking – exclusive access on state change
Make sure that a user always sees a legal state (never sees a temporary state)
Make sure that you have only legal->legal transitions
Java doesn’t guarantee fairness Locks are reentrant Wrong order of acquiring nested monitors
leads to deadlock Once deadlocked – threads will hang forever On wait(ms) you dunno whether it was a
notify() call or timeout that happened Thread scheduling provides no guarantees Lock contention is a scalability blocker
CAS stands for Compare-and-Swap Atomic instruction at CPU level (cmpxchg,
load and reserve, store conditional, etc.). Since Java SE 5 – adopted in Java to
allowing concurrent structures to be implemented. Old
value
=?
Ignore Set it
Setting a reference to an existing object is an atomic operation
Object construction is not an atomic operation!
Too low level – hard to implement high level structures (like latch, rendezvous and barrier)
Can’t pass a lock between threads Can’t attach a lock to an Object while no
thread is working with it
Lock contention – too many threads/CPUs Multiple-request data changes Fine-grained locking Keeping a lock without wasting a thread
The java.util.concurrent package aims to do for concurrency what java.util.Collections did for data structures. It makes◦ Some problems go away◦ Some problems trivial to solve by everyone◦ Some problems easier to solve by concurrent programmers◦ Some problems possible to solve by experts
Whenever you are about to use... Object.wait, notify, notifyAll, synchronized, new
Thread(); Check first if there is a class that ...
◦ automates what you are trying to do, or ◦ would be a simpler starting point for your own solution
java.util.concurrent is based on over 3 years experience with edu.oswego.cs.dl.util.concurrent
Many refactorings and functionality improvements
Additional native/JVM support ◦ Timing, atomics, built-in monitor extensions
Executors, Thread pools, and Futures Queues and blocking queues Timing Locks and Conditions Synchronizers: Semaphores, Barriers, etc Atomic variables Miscellany
Flexibility at expense of verbositylock.lock();
try {
action();
}
finally {
lock.unlock();
}
Overcomes limitations of synchronized◦ Doesn’t force block structured locking/unlocking◦ Allow interruptible lock acquisition and “try lock”◦ Can define customized implementations◦ Fair/Unfair
interface Lock {
void lock();
void lockInterruptibly() throws IE;
boolean trylock();
boolean trylock(long timeout,
TimeUnit unit)throws IE;
void unlock();
Condition newCondition();
}
Concrete ReentrantLock implementation◦ Fast, scalable with synchronized block semantics,
and additional query methods◦ Also FairReentrantLock subclass with slower but
more predictable first-in-first-out arbitration
class ParticleUsingLock { private int x, y; private final Random rng = new Random();private final Lock lock = new ReentrantLock(); public void move() throws InterruptedException { lock.lockInterruptibly(); // allow cancellation try { x += rng.nextInt(3) - 1; y += rng.nextInt(3) – 1; } finally { lock.unlock(); } } public void draw(Graphics g) { int lx, ly; lock.lock(); // no interrupts – AWT Event Thread try { lx = x; ly = y; } finally { lock.unlock(); } g.drawRect(lx, ly, 10, 10); } }
interface ReadWriteLock { Lock readLock(); Lock writeLock();}
A pair of locks for enforcing multiple-reader, single-writer access◦ Each used in the same way as ordinary locks
Concrete ReentrantReadWriteLock◦ Almost always the best choice for apps◦ Each lock acts like a reentrant lock
Remember Lock.newCondition(); ? Condition factors out the Object monitor
methods (wait, notify and notifyAll)◦ into distinct objects to give the effect of having
multiple wait-sets per object◦ can combine them with the use of arbitrary Lock
implementations. Where a Lock replaces the use of synchronized methods and statements, a Condition replaces the use of the Object monitor methods.
interface Condition {
void await() throws IE;
void awaitUninterruptibly();
long awaitNanos(long nanos) throws IE;
boolean awaitUntil(Date deadline) throws IE;
void signal();
void signalAll();
}
Allows more than one wait condition per object◦ Even for built-in locks, via Locks utility class
Allows much simpler implementation of some classic concurrent designs
Example
A small collection of small classes that:◦ Provide good solutions to common special-
purpose synchronization problems◦ Provide better ways of thinking about designs
But worse ways when they don't naturally apply!
◦ Can be tricky or tedious to write yourself Semaphore CountDownLatch CyclicBarrier
Semaphores can be seen as permit holders◦ Create with initial number of permits ◦ acquire takes a permit, waiting if necessary◦ release adds a permit◦ But no actual permits change hands.◦ Semaphore just maintains the current count.
Can use for both “locking” and “synchronizing”◦ With initial permits=1, can serve as a lock◦ Useful in buffers, resource controllers◦ Use in designs prone to missed signals ◦ Semaphores “remember” past signals
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
A CountDownLatch is initialized with a given count
The await methods block until the current count reaches zero due to invocations of the countDown method, after which all waiting threads are released and any subsequent invocations of await return immediately. This is a one-shot phenomenon -- the count cannot be reset.
class Driver { // ... void main(int N) throws InterruptedException { final CountDownLatch startSignal = new CountDownLatch(1); final CountDownLatch doneSignal = new CountDownLatch(N); for (int i = 0; i < N; ++i) // Make threads new Thread() { public void run() { try { startSignal.wait(); doWork(); doneSignal.countDown(); } catch(InterruptedException ie) {} }}.start(); initialize(); startSignal.countDown(); // Let all threads proceed doSomethingElse(); doneSignal.await(); // Wait for all to complete cleanup(); }}
Example
j.u.c.atomic contains classes representing scalars supporting "CAS“
boolean compareAndSet(expectedV, newV)◦ Atomically set to newV if holding expectedV◦ Always used in a loop
Essential for writing efficient code on MPs◦ Nonblocking data structures, optimistic algorithms,
reducing overhead and contention when updates center on a single field
JVMs use best construct available on platform◦ Compare-and-swap, Load-linked/Store-conditional, Locks
j.u.c.a also supplies reflection-based classes that allow CAS on given volatile fields of other classes
class Random { // snippets private AtomicLong seed; Random(long s) { seed = new AtomicLong(s); } long next(){ for(;;) { long s = seed.get(); long nexts = s * ... + ...; if (seed.compareAndSet(s,nexts) return s; } } }
Faster and less contention in programs with a single Random accessed by many threads
Let’s see java.util.Random
There’s also an AtomicReference class Why not use that to create a non-blocking
stack? The idea is the same:
◦ The push() method observes the current top node◦ constructs a new node to be pushed on the stack◦ if the topmost node has not changed since
the initial observation, installs the new node◦ If the CAS fails, it means that another thread
has modified the stack, so the process starts again
Example
The Segments◦ The maps is split into segments, only they lock
The root link Retries before resorting to locking – size()
Basic debugging support is added (in jconsole and thread dumps) to detect deadlocks based on java.util.concurrent structures
Double-ended deques added
Looking at thread dump (Ctrl+Break)◦ Locked monitors are noted◦ Monitors to wait for are shown◦ Owned synchronizers are listed◦ Java-level (synchronized) deadlocks are auto
detected
These slides are for educational purpose only
In most of the cases synchronized blocks are sufficient!
Synchronized blocks modes is intentionally kept simple
java.util.concurrent is hard to debug and needlessly complex for simple scenarios!
Do not use java.util.concurrent except if you really HAVE to!
Three short articles from Brian Goetz:http://www-128.ibm.com/developerworks/java/library/j-jtp04186/http://www-128.ibm.com/developerworks/java/library/j-jtp11234/http://www-128.ibm.com/developerworks/java/library/j-jtp10264/
For further questions:Михаил Стойнов[email protected]/blogКрасимир Семерджиев[email protected] Java Users Groupjava-bg.orggroups.google.com/group/bg-jug