Sie sind auf Seite 1von 66

Java Concurrency

Java Team
Concurrency
New in Java 5: java.util.concurrent
Thread Pools, Task Scheduling
Concurrent Collections
Atomic variables
Locks, Conditions, Synchronizers
Enhance scalability, performance,
readability and
thread safety of Java applications
Provide richer set of concurrency
Threads
Threads are expensive to create, can require
500K of memory during creation
Your threads spend a lot of time context-
switching, even if you have 10 processors
Whenever you want to start a thread, don’t use
Threads:
use Executor
Disadvantage of using
Threads
Creating a new thread causes some
performance overhead .
Too many threads can lead to reduced
performance, as the CPU needs to switch
between these threads.
You cannot easily control the number of
threads, therefore you may run into out of
memory errors due to too many threads.
Why Concurrency?
wait(), notify(), and synchronize are
hard to use, specified at a low level,
can lead to poor performance
Lots of wheel reinventing
Reduced programming effort
Increased performance
Increased reliability
> Eliminate threading hazards such
as deadlock, starvation,
race conditions, or excessive
Goal of Concurrency
Do what Collections did for data structures
Allow development of thread-safe classes, such
as servlets, built on concurrent building blocks
(like ConcurrentHashMap)
Make some solved things easier for
masses
Make very hard things solvable by
experts
Concurrency Utilities
Task Scheduling Framework
Callable's and Future's
Synchronizers
Concurrent Collections
Atomic Variables
Locks
Nanosecond-granularity timing
Concurrency:
Task Scheduling
Framework
Task Scheduling
Framework
Executor/ExercuteService/Executors
framework supports
> standardizing invocation
> scheduling
> execution
> control of asynchronous tasks according to
a set of execution Policies
Executor is an interface
ExecutorService extends Executor
Why Prefer Executor?
Executor framework allows for a variety of
execution policies
Provides cancellation and shutdown support
Created via factory
Can customize shutdown hooks
Simpler interface
provides a way of de-coupling task
submission from the execution
Many Executor implementations impose some
sort of
Task Scheduling
(Executor)
Use anExecutor.execute(aRunnable)
Do NOT use new Thread(aRunnable).start()

Executor executor = anExecutor;


executor.execute(new RunnableTask1());
executor.execute(new RunnableTask2());

try
Executer interface
Executor, a simple interface that supports
launching new tasks.
ExecutorService, a subinterface of Executor,
which adds features that help manage the
lifecycle, both of the individual tasks and of the
executor itself.
ScheduledExecutorService, a subinterface of
ExecutorService, supports future and/or periodic
execution of tasks.
Executor Interface
functions
execute(Runnable command)
Executes the given command at some time in
the future.
Executor and
ExecutorService
ExecutorService adds lifecycle
management
public interface Executor
{
void execute(Runnable command);
}
public interface ExecutorService extends
Executor
{
void shutdown();
Executor Service
ExecutorService supports graceful and
immediate shutdown
Has a lifecycle
Has many useful utility methods
Can get a single threaded executor
Bad Example: Webserver in a while() loop
listens forever and starts a new thread
for each request.
Executor Service
Methods
submit(Callable<T> task)
Submits a value-returning task for execution
and returns a Future representing the pending
results of the task.
submit(Runnable task)
Submits a Runnable task for execution and
returns a Future representing that task.
submit(Runnable task, T result)
Submits a Runnable task for execution and
returns a Future representing that task that will
upon completion return the given result.
Creating ExecutorService From
Executors
public class Executors
{
static ExecutorService newSingleThreadedExecutor();
static ExecutorService newFixedThreadPool(int n);
static ExecutorService newCachedThreadPool(int n);
static ScheduledExecutorService
newScheduledThreadPool(int n);
// additional versions specifying ThreadFactory
// additional utility methods
}
Using Executor
Executor pool =
Executor.newFixedThreadPool(7);
…socket.accept(); …
Runnable r = new Runnable() {
//handle request
}
pool.execute();
pre-J2SE 5.0 Code
Web
classServer—poor
WebServer resource management

{
public static void main(String[] args)
{
ServerSocket socket = new
ServerSocket(80);
while (true) {
final Socket connection =
socket.accept();
Runnable r = new Runnable() {
Executors Example
Web Server—better resource management
class WebServer
{
Executor pool
=Executors.newFixedThreadPool(7);
public static void main(String[] args)
{
ServerSocket socket = new
ServerSocket(80);
while (true) {
final Socket connection =
ScheduledExecutorService

SES is a drop-in replacement for


Timer Supports pooling

final ScheduledFuture<?>
beeperHandle =
scheduler.scheduleAtFixedRate(beeper,
10, 10, SECONDS);
scheduler.schedule(new Runnable()
{
Functions
schedule(Callable<V> callable, long delay,
TimeUnit unit)
Creates and executes a ScheduledFuture that
becomes enabled after the given delay.
scheduleAtFixedRate(Runnable command,
long initialDelay, long period, TimeUnit unit)
Creates and executes a periodic action that
becomes enabled first after the given initial
delay, and subsequently with the given period;
scheduleWithFixedDelay(Runnable
 command, long initialDelay, long delay,
Concurrency:Callables
and Futures
Callable's and
Future's:
If a new thread (callable thread) is started in an
Problem
application, there(pre-J2SE
is currently no way to return
a result from that thread to the thread (calling
5.0)
thread) that started it without the use of a
shared variable and appropriate synchronization
> This is complex and makes code harder to
understand and maintain
Interface Callable
Functional analog of Runnable.
It is similar to Runnable, in that both are
designed for classes whose instances are
potentially executed by another thread. A
Runnable, however, does not return a result
and cannot throw a checked exception.
public interface Callable<V>
{
V call() throws Exception;
}
Callables and Futures
Callable thread (Callee) implements Callable
interface
> Implement call() method rather than run()
Calling thread (Caller) submits Callable object to
Executor and then moves on
> Through submit() not execute()
> The submit() returns a Future object
Calling thread (Caller) then retrieves the result
using get() method of Future object
> If result is ready, it is returned
Runnable vs Callable
Runnable Callable

class c1 implements class c1 implements


Runnable Callable
{ {
public void run() public String call() throws
Exception
{
{
//
//
}
return null;
}
}
Future
Holds result of asynchronous call, normally a
Callable. Can find out if this task is
complete, cancelled, etc.

Implementing caches is tricky--often we’re


just moving the bottleneck from computation
to serialization most of the time.
Future functions
cancel(boolean mayInterruptIfRunning)
Attempts to cancel execution of this task.
get()
Waits if necessary for the computation to
complete, and then retrieves its result.
get(long timeout, TimeUnit unit)
Waits if necessary for at most the given time for
the computation to complete, and then
retrieves its result, if available.
isCancelled()
Build CallableExample
(This is Callee)
class CallableExample implements
Callable<String>
{
public String call() {
String result = “The work is ended”;
/* Do some work and create a result */
return result;
}
Future Example
(Caller)
class FutureDemo
{
public static void main(String[] args){
ExecutorService es
=Executors.newSingleThreadExecutor();
Future<String> f =es.submit(new
CallableExample());
/* Do some work in parallel */
try {
Concurrency:
Synchronizers
Semaphores
Typically used to restrict access to fixed size
pool of
resources
New Semaphore object is created with same
count
as number of resources
Thread trying to access resource calls aquire()
> Returns immediately if semaphore count
>0
> Blocks if count is zero until release() is
Semaphore
A counting semaphore. Conceptually, a
semaphore maintains a set of permits. Each
acquire() blocks if necessary until a permit is
available, and then takes it. Each release() adds
a permit, potentially releasing a blocking
acquirer.

A Semaphore is a thread synchronization


construct that can be used either to send
signals between threads to avoid missed signals
, or to guard a critical section like you would
Semaphore Example
private Semaphore available;
private Resource[] resources;
private boolean[] used;
public Resource(int poolSize) {
available = new Semaphore(poolSize);
/* Initialise resource pool */
}
public Resource getResource() {
try { available.aquire() } catch (IE) {}
/* Acquire resource */
}
public void returnResource(Resource r) {
/* Return resource to pool */
available.release();
}
Synchronizers
Utility classes
Low level
Latches
A latch is a synchronizer that can delay the progress of
threads until it reaches its terminal state. A latch acts as
a gate: until the latch reaches the terminal state the
gate is closed and no thread can pass, and in the
terminal state the gate opens, allowing all threads to
pass. Once the latch reaches the terminal state, it
cannot change state again, so it remains open forever.
Latches can be used to ensure that certain activities do
not proceed until other one-time activities complete,
such as:
Latches (Cont.)
Ensuring that a computation does not proceed until
resources it needs have been initialized. A simple
binary (two-state) latch could be used to indicate
"Resource R has been initialized", and any activity
that requires R would wait first on this latch.
Ensuring that a service does not start until other
services on which it depends have started. Each
service would have an associated binary latch;
starting service S would involve first waiting on the
latches for other services on which S depends, and
then releasing the S latch after startup completes
so any services that depend on S can then proceed.
New Synchronizers:
Semaphor: Represents a permit manager
CountdownLatch: Gate with one or more
padlocks -- allows 1 or more threads to wait for
a set of threads to complete action
Exchanger: Allows two threads to rendezvous
and exchange data, then go their separate
ways.
CountDownLatch

CountDownLatch is a flexible latch


implementation that can be used in any of
these situations; it allows one or more threads
to wait for a set of events to occur. The latch
state consists of a counter initialized to a
positive number, representing the number of
events to wait for. The countDown method
decrements the counter, indicating that an
event has occurred, and the await methods wait
for the counter to reach zero, which happens
when all the events have occurred. If the
Barriers
This is a synchronization aid that allows a set of
threads to all wait for each other to reach a
common barrier point. The interface to the
barrier is the CyclicBarrier class, called cyclic
because it can be re-used after the waiting
threads are released. This is useful for parallel
programming.

Barriers are similar to latches in that they block


a group of threads until some event has
occurred. The key difference is that with a
CyclicBarrier
CyclicBarrier allows a fixed number of parties to
rendezvous repeatedly at a barrier point and is useful in
parallel iterative algorithms that break down a problem
into a fixed number of independent subproblems.
Threads call await when they reach the barrier point, and
await blocks until all the threads have reached the
barrier point. If all threads meet at the barrier point, the
barrier has been successfully passed, in which case all
threads are released and the barrier is reset so it can be
used again.
CyclicBarrier (Cont.)
If a call to await times out or a thread blocked in
await is interrupted, then the barrier is considered
broken and all outstanding calls to await terminate
with BrokenBarrierException. If the barrier is
successfully passed, await returns a unique arrival
index for each thread, which can be used to "elect"
a leader that takes some special action in the next
iteration. CyclicBar rier also lets you pass a barrier
action to the constructor; this is a Runnable that is
executed (in one of the subtask threads) when the
barrier is successfully passed but before the blocked
threads are released.
Difference Between
Latch and Barrier
Barrier: This is a synchronization aid that
allows a set of threads to all wait for each other
to reach a common barrier point. The interface
to the barrier is the CyclicBarrier class, called
cyclic because it can be re-used after the
waiting threads are released. This is useful for
parallel programming.
 
CountDown Latch: A latch is a condition
starting out false, but once set true remains
true forever. The
java.util.concurrent.CountDownLatch class
Exchanger
Allows two threads to exchange objects at a rendezvous
point, and can be useful in pipeline designs. Each thread
presents some object on entry to the exchange() method
and receives the object presented by the other thread on
return. As an example, consider the classical consumer-
producer problem (two entities share a channel);
Concurrency:
Concurrent
Collections
Concurrent Collections
Pre Java 5 was thread-safe but not concurrent.

Every method was synchronized. This was okay because


we weren’t on multi-core machines.

Azul has a 384-way box now--running Java on that would


have been problematic.

Old collections required locking during iteration.


New:
ConcurrentHashMap
Drop-in replacement for Hashtable
No iterator throws
ConcurrentModificationException
Allows read/write overlaps.
It is therefore weakly concurrent
New:
CopyOnWriteArrayList
Locking during iteration hurts scalability.
Changes made by other threads may or may
not be noticed by reads
New: BlockingQueue
A blocking queue is a queue that blocks when
you try to dequeue from it and the queue is
empty, or if you try to enqueue items to it and
the queue is already full.
Extends Queue to provide two operations:
take(): wait for queue to be non-empty
put(): wait for queue to have room
BlockingQueue
Interface
Provides thread safe way for multiple threads to
manipulate collection
ArrayBlockingQueue is simplest concrete
implementation
Full set of methods
> put()
> offer() [non-blocking]
> peek()
> take()
Blocking Queue
Example 1
private BlockingQueue<String> msgQueue;
public Logger(BlockingQueue<String> mq)
{ msgQueue = mq; }
public void run()
{
try {
while (true) {
String message = msgQueue.take();
/* Log message */
Blocking Queue
Example 2
private ArrayBlockingQueue messageQueue =new
ArrayBlockingQueue<String>(10);
Logger logger = new Logger(messageQueue);
public void run()
{
String someMessage;
try {
while (true) {
/* Do some processing */
/* Blocks if no space available */
Concurrency: Locks
Use of monitor
synchronization
Single wait-set per lock
is fine
for
No waymost apps
to interrupt a threadBUT:
waiting for a lock
No way to time out when waiting for a lock
Locking must be block-structured
Inconvenient to acquire a variable number of
locks at once.
Advanced techniques not possible
Lock objects address these concerns
Locks
Lock interface
> More extensive locking operations than synchronized
block
> No automatic unlocking – use try/finally to unlock
> Non-blocking access using tryLock()
ReentrantLock
> Concrete implementation of Lock
> Holding thread can call lock() multiple times and not
block
> Useful for recursive code
Lock Objects
Address synchronization monitor issues
Provide a high-level interface:
lock
tryLock (tells you if you got it)
unlock
newCondition (every object can’t provide
conditions)
ReentrantLock
High-Performance
Re-entrant, mutual exclusion lock offers same
semantics as synchronized but with extra
features
Can interrupt a thread waiting to acquire a lock
Can specify a timeout while waiting
Can poll for lock availability
Downside: have to manually unlock (in finally
{})
Re-entrant Lock Example
Lock lock = new ReentrantLock();

lock.lock();
try {
//do work
} catch …{
//resolve invariants and rethrow }
finally {
lock.unlock(); }
ReadWriteLock
Has two locks controlling read and write access
> Multiple threads can acquire the read lock if no threads
have a write lock
> If a thread has a read lock, others can acquire read lock
but nobody can acquire write lock
> If a thread has a write lock, nobody can have read/write lock
> Methods to access locks
rwl.readLock().lock();
rwl.writeLock().lock();
ReadWriteLock
Multiple reader, single writer exclusion lock
ReadWriteLock class defines a pair of locks:
interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
Use for frequent, long reads w/ few writes.
Various implementation policies
ReadWrite Lock
Example
class ReadWriteMap
{
final Map<String, Data> m = new
TreeMap<String, Data>();
final ReentrantReadWriteLock rwl =new
ReentrantReadWriteLock();
final Lock r = rwl.readLock();
final Lock w = rwl.writeLock();
public Data get(String key)
Condition Interface
Conditions (also known as condition queues or
condition variables) provide a means for one
thread to suspend execution (to "wait") until
notified by another thread that some state
condition may now be true. Because access to
this shared state information occurs in different
threads, it must be protected, so a lock of some
form is associated with the condition. The key
property that waiting for a condition provides is
that it atomically releases the associated lock
and suspends the current thread, just like
Object.wait.
Atomic Variables
Until JDK 5.0, it was not possible to write wait-
free, lock-free algorithms in the Java language
without using native code. With the addition of
the atomic variables classes in the
java.util.concurrent.atomic package, that has
changed. The atomic variable classes all expose
a compare-and-set primitive (similar to
compare-and-swap), which is implemented
using the fastest native construct available on
the platform (compare-and-swap, load
linked/store conditional, or, in the worst case,
spin locks).
Few Classes in Atomic
AtomicBoolean
AtomicInteger
AtomicIntegerArray
AtomicIntegerFieldUpdater
AtomicLong
AtomicLongArray
Thank You.

Das könnte Ihnen auch gefallen