Java Concurrency, Memory Model, and Trends

download Java Concurrency, Memory Model, and Trends

If you can't read please download the document

Transcript of Java Concurrency, Memory Model, and Trends

Java
Concurrency

Carol McDonaldAvaility

Speakers Qualifications

Carol cDonald: Previous work: Java Architect at Sun Microsystems

Application to manage car Loans for Toyota (>10 million loans)

Pharmaceutical Intranet (Roche Switzerland)

Telecom Network Mgmt (Digital France)

X.400 Email Server (IBM Germany)

Java Concurrency

Lets look at some of the changes and new features in the Java Virtual Machine

Why Concurrency:

Benefits of threads:Multitasking, Exploit multiple cores or CPUs

Multi-threading good for blocking for I/O or other blocking ops

Java < 1.5 concurrency primitives:wait(), notify(), sleep(), interrupt(), synchronized

Easy to use incorrectlyIncorrect use can produce poor performance

Previously Java has supported several very primitive mechanisms for co-ordinating a number of threads. The synchronized keyword can be applied to methods or code blocks and the Thread class supports the wait, notify, sleep and interrupt mothods. The problem with these primitive mechanisms is that they are just that: primitive. As we will see the new utilities will greatly enhance Java applications in terms of scalability, performance, readability and thread safety.

Risks of Threads

Safety hazardsSyncronization, contention

Liveness hazardsRace conditions, deadlock

Performance hazardsContext switching overhead

Concurrency Utilities Goals

set of concurrency building blockslibrary for concurrency like Collections for data structures

Enhance scalability, performance, and thread safety

Java Memory Model for Multi tasking

The idea behind the new concurrency utilities is to provide a much richer set of functions that programmers can use to create simple and powerful multi-threaded applications, in the same way that the Collections classes provided a much richer set of data structure based APIs.

By providing much finer granularity of locking and different approaches such as multiple read-single write locks a secondary aim is to enable the performance of a multi-threaded Java application to match or exceed that of native C applications in the high-end server environment.

Previously Java has supported several very primitive mechanisms for co-ordinating a number of threads. The synchronized keyword can be applied to methods or code blocks and the Thread class supports the wait, notify, sleep and interrupt mothods. The problem with these primitive mechanisms is that they are just that: primitive. As we will see the new utilities will greatly enhance Java applications in terms of scalability, performance, readability and thread safety.

Do you see the error?

01 class PingPong {02 public static synchronized void main(String[] a) {03 Thread t = new Thread() {04 public void run() {05 pong(); 06 }07 };08 09 t.run();10 System.out.print("Ping");11 }1213 static synchronized void pong() {14 System.out.print("Pong");15 }16 }

What Does It Print?

(a) PingPong(b) PongPing(c) It varies

What Does It Print?

(a) PingPong(b) PongPing(c) It varies

Not a multithreaded program!

Example How to start a 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(); }

}

Another Look

01 class PingPong {02 public static synchronized void main(String[] a) {03 Thread t = new Thread() {04 public void run() {05 pong(); 06 }07 };08 09 t.run(); // Common typo!10 System.out.print("Ping");11 }1213 static synchronized void pong() {14 System.out.print("Pong");15 }16 }

How Do You Fix It?

01 class PingPong {02 public static synchronized void main(String[] a) {03 Thread t = new Thread() {04 public void run() {05 pong(); 06 }07 };0809 t.start();10 System.out.print("Ping");11 }1213 static synchronized void pong() {14 System.out.print("Pong");15 }16 }

The Moral

Invoke Thread.start, not Thread.run

Common error

Can be very difficult to diagnose

Concurrency Utilities:

Task Scheduling Framework: Executor interface replaces direct use of Thread

Callable and Future

SynchronisersSemaphore, CyclicBarrier, CountDownLatch

Concurrent collections

Lock

Atomic, Visible

Here is a list of things we'll talk about in this session. This is not an exhaustive list of all the new features, but given the limited time we have this should give you a good grasp of the main areas of functionality.One of the main changes in using the new concurrency utilities is the concept of moving away from interacting directly with a thread object. As we'll see the new and preferred way is through an interface called ExecutorService. There are several factory methods available to easily provide the programmer with standardised mechanisms for the Executor such as thread pools, single thread and priority threads.# Task Scheduling Framework - The Executor framework is a framework for standardizing invocation, scheduling, execution, and control of asynchronous tasks according to a set of execution policies. Implementations are provided that allow tasks to be executed within the submitting thread, in a single background thread (as with events in Swing), in a newly created thread, or in a thread pool, and developers can create of Executor supporting arbitrary execution policies. The built-in implementations offer configurable policies such as queue length limits and saturation policy which can improve the stability of applications by preventing runaway resource consumption.# Concurrent Collections - Several new Collections classes have been added, including the new Queue and BlockingQueue interfaces, and high-performance, concurrent implementations of Map, List, and Queue.

Until now it has required relatively complex coding to allow a child thread to return a result to the its parent. This is even more complex when it is necessary to synchonize the threads so that the parent can only continue when the child has completed generatijng the result. This becomes very simple now through the use of Callable and Future.

Semaphores are a well understood mechanism that are now supported on Java.

BlockingQueues allow simple data structures to be used by multiple threads in a concurrent way such that the programmer is not responsible for ensuring safe concurrent access.

Lastly the idea of an Atomic variable that can safely be accessed and modified is also iincluded in the concurrency utilities.

Concurrency Utilities:

Task Scheduling Framework: Executor

Separate task submission from execution policy

No more direct Thread invocationUse myExecutor.execute(aRunnable);

Not new Thread(aRunnable).start();

public interface Executor { void execute (Runnable command); }

Here is a list of things we'll talk about in this session. This is not an exhaustive list of all the new features, but given the limited time we have this should give you a good grasp of the main areas of functionality.One of the main changes in using the new concurrency utilities is the concept of moving away from interacting directly with a thread object. As we'll see the new and preferred way is through an interface called ExecutorService. There are several factory methods available to easily provide the programmer with standardised mechanisms for the Executor such as thread pools, single thread and priority threads.# Task Scheduling Framework - The Executor framework is a framework for standardizing invocation, scheduling, execution, and control of asynchronous tasks according to a set of execution policies. Implementations are provided that allow tasks to be executed within the submitting thread, in a single background thread (as with events in Swing), in a newly created thread, or in a thread pool, and developers can create of Executor supporting arbitrary execution policies. The built-in implementations offer configurable policies such as queue length limits and saturation policy which can improve the stability of applications by preventing runaway resource consumption.# Concurrent Collections - Several new Collections classes have been added, including the new Queue and BlockingQueue interfaces, and high-performance, concurrent implementations of Map, List, and Queue.

Until now it has required relatively complex coding to allow a child thread to return a result to the its parent. This is even more complex when it is necessary to synchonize the threads so that the parent can only continue when the child has completed generatijng the result. This becomes very simple now through the use of Callable and Future.

Semaphores are a well understood mechanism that are now supported on Java.

BlockingQueues allow simple data structures to be used by multiple threads in a concurrent way such that the programmer is not responsible for ensuring safe concurrent access.

Lastly the idea of an Atomic variable that can safely be accessed and modified is also iincluded in the concurrency utilities.

Executor
an object whose job it is to run Runnables:

public interface Executor { void execute (Runnable command); }

public interface ExecutorService extends Executor {..}

public class Executors { static ExecutorService newFixedThreadPool(int poolSize);...}

Executor pool = Executors.newFixedThreadPool(5);pool.execute (runnable);

manages the lifecycle of the execution service

static factory methods for Executor implementations

As mentioned earlier programmers should now not interact directly with the Thread class. Before, we would create a class that implemented the Runnable interface. To start this in a new thread we would use a line like this: create a new thread with using the Runnable class and call the start method that would in turn call the run method in our class. This is still quite correct, but the idea is to replace this with an abstracted interface, Executor. Instead of calling start we call execute.

Since this is abstracted away from the Thread class it becomes a simple task to change the way we handle the threading should we wish to do so at a later date. For example, we could start with a piece of code that creates a single thread to execute our new code. As requirements and processing power change we find that we need to run a number of threads for our class. We can simply change the factory method we use to create a thread pool and we are then able to use the same class in a number of threads rather than just one.

Creating Executors

Factory methods in the Executors class

public class Executors { static ExecutorService newSingleThreadedExecutor(); static ExecutorService newFixedThreadPool(int poolSize); static ExecutorService newCachedThreadPool(); static ScheduledExecutorService newScheduledThreadPool(); // Other methods not listed}

How not to manage tasks

class UnreliableWebServer { public static void main(String[] args) { ServerSocket socket = new ServerSocket(80); while (true) { final Socket connection = socket.accept(); Runnable r = new Runnable() { public void run() { handleRequest(connection); } }; // Don't do this! new Thread(r).start(); } }}

Could create too many threads !

Thread Pool Example

class WebService { public static void main(String[] args) { Executor pool = Executors.newFixedThreadPool(5); ServerSocket socket = new ServerSocket(999); for (;;) { final Socket connection = socket.accept(); Runnable task = new Runnable() { public void run() { new Handler().process(connection); } } pool.execute (task); } } } class Handler { void process(Socket s); }

myExecutor.execute(aRunnable)

Here is an example of code that uses the new Executor, Executors and ExecutorService classes.

The example is a standard web service class that needs to handle a number of incoming connections simultaneously through a number of separate threads. The number of threads needs to be bounded to prevent the system from running out of resources when the load becomes too high. Previously it would have been necessary to create your own thread pooling class that would create a set of threads and then manage all of the alloaction and deallocation of those threads with all of the required concurrent access controls.

With the concurrency utilities this is all provided by default.

In the main routine we initialise a new fixed thread pool with a size of 7. We use the newFixedThreadPool method of the Executors class. This will return a ExecutorService object. Since ExecutorService implements the Executor interface we can assign it to an Executor object reference.

To handle an incoming connection we simply call execute on our pool object passing it a Runnable object (which in this case is defined through an inner class). The run method does whatever work we need the thread to do. Whenever connections come in they will be allocates a thread from the pool. When the run method completes the thread will automatically be returned to the pool. If a connection comes in and all threads are in use the main loop will block until a thread is freed.

ExecutorService for Lifecycle Support

ExecutorService supports graceful and immediate shutdown

public interface ExecutorService extends Executor { void shutdown(); List shutdownNow(); boolean isShutdown(); boolean isTerminated(); boolean awaitTermination(long timeout, TimeUnit unit); // additional methods not listed}

Callables and Futures

Callable interface provides way to get a result from a thread

Implement call() method rather than run()

Callable is submitted to ExecutorService

Call submit() not execute()

submit() returns a Future object

Retrieve result using get() method of Future object

If result is ready it is returned

If not ready calling thread will block

If a new Thread is started in an application there is currently no way to return a result from that thread to the 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.

The Callable interface allows a result to be returned easily to a parent thread or an exception to be sent to indicate some problem.

To use a Callable you define a class that implements the Callable interface, in the same way that you would define a class that implements Runnable for a new thread. The Callable interface defines one method, call. The return type of the call method is defined by the type argument of the Callable interface specified for the class definition, i.e. Callable is a generic interface. We will see how this works in reality in the next example. The call method contains whatever instructions are required to generate the result that will be returned.

The Callable is passed to an ExecutorService through the submit method, rather than using the execute method of the Executor interface. The submit method will return a Future object immediately.

The parent thread can continue concurrently with the Callable. When the parent thread requires the result of the Callable it calls the get method of the Future object. If the Callable has completed the result will be returned immediatley. If the Callable has not completed its work, the parent thread will block until the result is available.

The submit method may also be called with a Runnable rather than a Callable. In this case get will always return null (but may block until the Runnable has completed its work). There is another form of the submit method that takes a Runnable as an argument and also takes a result as an argument. When the Runnable completes the result will be returned from the Future via the get method. This is useful if the Future is to be passed to another method.

Callable Example

class CallableExample implements Callable {

public String call() { String result = null;

/* Do some work and create a result */

return result; }}

This shows a simple example of a Callable object.

Since Callable is generic we specify the type argument of Callable here as a String, so that the return type of the call method is also String.

The call method will do some work to generate a result and then return the appropriate String.

Future Example

ExecutorService es = Executors.newSingleThreadedExecutor();Future f = es.submit (new CallableExample());

/* Do some work in parallel */

try { String callableResult = f.get();} catch (InterruptedException ie) { /* Handle */} catch (ExecutionException ee) { /* Handle */}

Here we see an example of the use of the CallableExample with a Future.

Firstly we create a new ExecutorService using a factory method from the Executors class.

Next we submit a new CallableExample to the ExecutorService. This returns a Future which we must specify the type argument of. Obviously this must be the same type as the type argument of the Callable (which is String). If we did not we would get a compiler error.

After we do some work in this thread we need to access the result of the CallableExample thread. To do this we simply call f.get. If the call method has got to the return statement we will immediately get the result. If not this thread will block until the call method of the CallableExample object calls return. We handle exceptions as appropriate. An ExecutionException is thrown if the call method threw an exception rather than returning a result. The cause of the exception can be determined via the Throwable.getCause() method.

ScheduledExecutorService

Deferred and recurring tasksSchedule execution of Callable or Runnable to run once after a fixed delayschedule()

Schedule a Runnable to run periodically at a fixed ratescheduleAtFixedRate()

Schedule a Runnable to run periodically with a fixed delay between executionsscheduleWithFixedDelay()

Submission returns a ScheduledFutureCan be used to cancel task

ScheduledExecutorService Example

ScheduledExecutorService sched = Executors.newSingleThreadScheduledExecutor();

public void runTwiceAnHour(long howLong) { final Runnable rTask = new Runnable() { public void run() { /* Work to do */ } }; final ScheduledFuture rTaskFuture = sched.scheduleAtFixedRate(rTask, 0, 1800, SECONDS); sched.schedule(new Runnable { public void run { rTaskFuture.cancel(true); } }, howLong, SECONDS);}

Recommendation

Hold Locks for as short a time as possible

Do not perform CPU intensive or I/O ops inside a synchronized method or while locking

Synchronize Critical Section

E.g., shared resource is an customer account. Certain methods called by multiple threads.

Hold monitor lock for as short a time as possible.

synchronized double getBalance() {Account acct = verify(name, password);return acct.balance;}

Lock held for long time

double getBalance() {synchronized (this) {Account acct = verify(name, password);return acct.balance;}}

Current object is locked

Equivalent to above

double getBalance() {Account acct = verify(name, password);synchronized (acct) { return acct.balance};}

Better

Only acct object is locked for shorter time

Locks

Java provides basic locking via synchronized

Good for many situations, but some issuesSingle monitor per object

Not possible to interrupt thread waiting for lock

Not possible to time-out when waiting for a lock

Block structured approachAquiring multiple locks is complex

Advanced techniques not possible

New Lock interface addresses these issues

Lock Interface

No automatic unlocking

Interface Lock { void lock(); void lockInterruptibly() throws IE; boolean tryLock(); boolean tryLock(long t, TimeUnit u) throws IE; //returns true if lock is aquired

void unlock(); Condition newCondition() throws UnsupportedOperationException;}

IE = InterruptedException

The Lock interface provides more extensive locking operations than using a synchronized block. Because we are using methods of a class to explicitly perform the lock and unlock operations the programmer needs to be more careful in its use. With a synchronized block of code it is impossible to forget to unlock it. Once the code execution leaves that block whether normally or through some for of exception, the lock is released. Using the Lock class the programmer must typically use a try-finally construct and put the unlock call within the finally clause to ensure that the lock is always released.

One advantage of the Lock interface over synchronized is the ability to not block if a lock is not available. The tryLock method will always return immediately, returning true if the lock was aquired or false if not. This method may also be called with a timeout parameter so the thread will only block for the specified time if the lock is not aquired.

ReentrantLock provides a concrete implementation of the Lock interface. The thread that holds the lock can call the lock method multiple times without blocking. This is especially useful in recursive code that needs to protect access to a certain section of code.

RentrantLock

Simplest concrete implementation of Lock

Generally better performance under contention than synchronized

Remember Lock is not automatically releasedMust use a finally block to release

Multiple wait-sets supportedUsing Condition interface

ReentrantLock provides a concrete implementation of the Lock interface. The thread that holds the lock can call the lock method multiple times without blocking. This is especially useful in recursive code that needs to protect access to a certain section of code.

Lock Example

Lock lock = new RentrantLock();

public void accessProtectedResource() throws IllegalMonitorStateException { lock.lock();

try { // Access lock protected resource } finally { // Ensure lock is always released lock.unlock(); }}

ReadWriteLock Interface

Has two locks controlling read and write accessMultiple threads can aquire the read lock if no threads have a write lock

Only one thread can aquire the write lock

Methods to access locks

rwl.readLock().lock(); rwl.writeLock().lock();Better performance for read-mostly data access

The ReadWriteLock permits multiple threads to have read access to a protected piece of code, but only one thread may access the code in write mode. Effectively the ReadWriteLock consists of two locks that are implemented as inner classes. If a thread aquires the read lock other threads may also aquire the read lock. If a read lock is held no thread may aquire the write lock until all read locks have been released. If a thread holds the write lock, no thread may aquire either the write lock or a read lock.

ReadWriteLock is an interface. RentrantReadWriteLock is a concrete implementation of this interface.

ReadWriteLock Example

ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();Lock r = rwl.readLock();Lock w = rwl.writeLock();ArrayList data = new ArrayList();

public String getData(int pos) { r.lock(); try { return data.get(pos); } finally { r.unlock(); }}public void addData(int pos, String value) { w.lock(); try { data.add(pos, value); } finally { w.unlock(); }}

Synchronizers

Co-ordinate access and control -can eliminate most uses of wait or notify

SemaphoreManages a fixed sized pool of resources

CountDownLatchOne or more threads wait for a set of threads to complete an action

CyclicBarrierSet of threads wait until they all reach a specified point

ExchangerTwo threads reach a fixed point and exchange data

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) {} } public void returnResource(Resource r) { /* Return resource to pool */ available.release(); }

Here we see an example of the use of a semaphore.

We want to control access to a fixed size pool of resources so that multiple threads can request the use of a resource and return it when they have finished with it. Since there will be multiple threads involved we need to ensure thread safety. The semaphore can do this for us in a simple way.

In the creator we create a new semaphore with the same size as the pool of resources we're creating.

When a thread requests a resource we call the aquire method of the semaphore to see if we can aquire a permit to use a resource. If there are resources available this will return and a resource will be returned from the pool. If all the resources are in use the call to aquire will block until another thread calls release on the semaphore.

When a thread finishes with a resource the resource is returned to the pool and the release method is called. Both aquire and release can be considered atomic operations.

NOTE: This is not a complete code sample, it only demonstrates the use of the semaphore. How the next resource is allocated and resources are returned to the pool would also need to be made thread safe using additional code.

Recommendation

Prefer Concurrency utilities to wait and notify

BlockingQueue Interface

thread safe Producer Consumer Pattern

Interface BlockingQueue { void put(E o) throws IE; boolean offer(E o) throws IE; boolean offer(E o, long t, TimeUnit u) throws IE; E take() throws IE; E poll() throws IE; E poll(long t, TimeUnit u) throws IE; int drainTo(Collection