Concurrent Java
Concurrent Java
1. Concurrency
1.1. What is concurrency?
1.2. Process vs. threads
2. Improvements and issues with concurrency
2.1. Limits of concurrency gains
2.2. Concurrency issues
3. Concurrency in Java
3.1. Processes and Threads
3.2. Locks and thread synchronization
3.3. Volatile
4. The Java memory model
4.1. Overview
4.2. Atomic operation
4.3. Memory updates in synchronized code
5. Immutability and Defensive Copies
5.1. Immutability
5.2. Defensive Copies
6. Threads in Java
7. Threads pools with the Executor Framework
8. Futures and Callables
9. Nonblocking algorithms
10. Fork-Join in Java 7
11. Deadlock
13. Links and Literature
13.1. Source Code
13.2. Concurrency Resources
13.3. jspm Resources
1. Concurrency
1.1. What is concurrency?
Concurrency is the ability to run several programs or several parts of a program in parallel. If a time
consuming task can be performed asynchronously or in parallel, this improve the throughput and the
interactivity of the program.
A modern computer has several CPU's or several cores within one CPU. The ability to leverage these
multi-cores can be the key for a successful high-volume application.
A Java application runs by default in one process. Within a Java application you work with several
threads to achieve parallel processing or asynchronous behavior.
Liveness failure: The program does not react anymore due to problems in the concurrent access
of data, e.g. deadlocks.
3. Concurrency in Java
3.1. Processes and Threads
A Java program runs in its own process and by default in one thread. Java supports threads as part of
the Java language via theThread code. The Java application can create new threads via this class.
Java 1.5 also provides improved support for concurrency with the in the
java.util.concurrent
package.
Java provides locks to protect certain parts of the code to be executed by several threads at the same
time. The simplest way of locking a certain method or Java class is to define the method or class with
the synchronized keyword.
The synchronized keyword in Java ensures:
that only a single thread can execute a block of code at the same time
that each thread entering a synchronized block of code sees the effects of all previous
modifications that were guarded by the same lock
Synchronization is necessary for mutually exclusive access to blocks of and for reliable communication
between threads.
You can use the synchronized keyword for the definition of a method. This would ensure that only one
thread can enter this method at the same time. Another threads which is calling this method would wait
until the first threads leaves this method.
You can also use the synchronized keyword to protect blocks of code within a method. This block is
guarded by a key, which can be either a string or an object. This key is called the lock. All code which is
protected by the same lock can only be executed by one thread at the same time
For example the following datastructure will ensure that only one thread can access the inner block of
the add() and next()methods.
package de.jspm.pagerank.crawler;
import java.util.ArrayList;
import java.util.List;
/**
* Data structure for a web crawler. Keeps track of the visited sites and keeps
* a list of sites which needs still to be crawled.
*
* @author Lars Vogel
*
*/
/**
* Get next site to crawl. Can return null (if nothing to crawl)
*/
3.3. Volatile
If a variable is declared with the volatile keyword then it is guaranteed that any thread that reads the field
will see the most recently written value. The volatile keyword will not perform any mutual exclusive lock
on the variable.
As of Java 5 write access to a volatile variable will also update non-volatile variables which were
modified by the same thread. This can also be used to update values within a reference variable, e.g. for
a volatile variable person. In this case you must use a temporary variable person and use the setter to
initialize the variable and then assign the temporary variable to the final variable. This will then make the
address changes of this variable and the values visible to other threads.
An immutable class may have some mutable data which is uses to manages its state but from the
outside this class nor any attribute of this class can get changed.
For all mutable fields, e.g. Arrays, that are passed from the outside to the class during the construction
phase, the class needs to make a defensive-copy of the elements to make sure that no other object from
the outside still can change the data
//package de.jspm.performance.defensivecopy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Makes a defensive copy of the List and return it
* This way cannot modify the list itself
*
* @return List<String>
*/
6. Threads in Java
The base means for concurrency are is the
type java.lang.Runnable.
java.lang.Threads
is an interface with defines the run() method. This method is called by the Thread object and
contains the work which should be done. Therefore the "Runnable" is the task to perform. The Thread is
the worker who is doing this task.
Runnable
The following demonstrates a task (Runnable) which counts the sum of a given range of numbers.
Create a new Java project called de.jspm.concurrency.threads for the example code of this section.
package de.jspm.concurrency.threads;
/**
* MyRunnable will count the sum of the number from 1 to the parameter
* countUntil and then write the result to the console.
* <p>
* MyRunnable is the task which will be performed
*
* @author Lars Vogel
*
*/
MyRunnable(long countUntil) {
this.countUntil = countUntil;
}
@Override
public void run() {
long sum = 0;
for (long i = 1; i < countUntil; i++) {
sum += i;
}
System.out.println(sum);
}
}
Thread
package de.jspm.concurrency.threads;
import java.util.ArrayList;
import java.util.List;
}
int running = 0;
do {
running = 0;
for (Thread thread : threads) {
if (thread.isAlive()) {
running++;
}
}
System.out.println("We have " + running + " running threads. ");
} while (running > 0);
}
}
You cannot easily control the number of threads, therefore you may run into out of memory errors
due to too many threads.
The java.util.concurrent package offers improved support for concurrency compared to the direct usage
of Threads. This package is described in the next section.
Tip
If you want to use one thread pool with one thread which executes several
runnables you can use the Executors.newSingleThreadExecutor() method.
Create again the Runnable.
package de.jspm.concurrency.threadpools;
/**
* MyRunnable will count the sum of the number from 1 to the parameter
* countUntil and then write the result to the console.
* <p>
* MyRunnable is the task which will be performed
*
* @author Lars Vogel
*
*/
MyRunnable(long countUntil) {
this.countUntil = countUntil;
}
@Override
public void run() {
long sum = 0;
for (long i = 1; i < countUntil; i++) {
sum += i;
}
System.out.println(sum);
}
}
package de.jspm.concurrency.threadpools;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
In case the threads should return some value (result-bearing threads) then you can use
the java.util.concurrent.Callable class.
Runnables.
In case you expect your threads to return a computed result you can use
The Callable object allows to return values after completion.
java.util.concurrent.Callable.
The Callable object uses generics to define the type of object which is returned.
If you submit a Callable object to an Executor the framework returns an object of
type java.util.concurrent.Future. This Futureobject can be used to check the status of a Callable and to
retrieve the result from the Callable.
On the Executor you can use the method submit to submit a Callable and to get a future. To retrieve the
result of the future use the get() method.
package de.jspm.concurrency.callables;
import java.util.concurrent.Callable;
}
package de.jspm.concurrency.callables;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
}
long sum = 0;
System.out.println(list.size());
// now retrieve the result
for (Future<Long> future : list) {
try {
sum += future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
System.out.println(sum);
executor.shutdown();
}
}
9. Nonblocking algorithms
Java 5.0 provides supports for additional atomic operations. This allows to develop algorithm which are
non-blocking algorithm, e.g. which do not require synchronization, but are based on low-level atomic
hardware primitives such as compare-and-swap (CAS). A compare-and-swap operation check if the
variable has a certain value and if it has this value it will perform this operation.
Non-blocking algorithm are usually much faster then blocking algorithms as the synchronization of
threads appears on a much finer level (hardware).
For example this created a non-blocking counter which always increases. This example is contained in
the project calledde.jspm.concurrency.nonblocking.counter.
package de.jspm.concurrency.nonblocking.counter;
import java.util.concurrent.atomic.AtomicInteger;
}
public int increment(){
return value.incrementAndGet();
}
And a test.
package de.jspm.concurrency.nonblocking.counter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
incrementAndGet()
The JDK itself makes more and more use of non-blocking algorithms to increase performance for every
developer. Developing correct non-blocking algorithm is not a trivial task.
For more information on non-blocking algorithm, e.g. examples for a non-blocking Stack and non-block
LinkedList, please seehttp://www.ibm.com/developerworks/java/library/j-jtp04186/index.html
package algorithm;
import java.util.Random;
/**
*
* This class defines a long list of integers which defines the problem we will
* later try to solve
*
*/
public Problem() {
Random generator = new Random(19580427);
for (int i = 0; i < list.length; i++) {
list[i] = generator.nextInt(500000);
}
}
Define now the Solver class as shown in the following example coding.
Tip
The API defines other top classes, e.g. RecursiveAction, AsyncAction.
Check the Javadoc for details.
package algorithm;
import java.util.Arrays;
import jsr166y.forkjoin.RecursiveAction;
@Override
protected void compute() {
if (list.length == 1) {
result = list[0];
} else {
int midpoint = list.length / 2;
int[] l1 = Arrays.copyOfRange(list, 0, midpoint);
int[] l2 = Arrays.copyOfRange(list, midpoint, list.length);
Solver s1 = new Solver(l1);
Solver s2 = new Solver(l2);
forkJoin(s1, s2);
result = s1.result + s2.result;
}
}
}
package testing;
import jsr166y.forkjoin.ForkJoinExecutor;
import jsr166y.forkjoin.ForkJoinPool;
import algorithm.Problem;
import algorithm.Solver;
long sum = 0;
// check if the result was ok
for (int i = 0; i < test.getList().length; i++) {
sum += test.getList()[i];
}
System.out.println("Done. Result: " + sum);
}
}
11. Deadlock
A concurrent application has the risk of a deadlock. A set of processes are deadlocked if all processes
are waiting for an event which another process in the same set has to cause.
For example if thread A waits for a lock on object Z which thread B holds and thread B wait for a look on
object Y which is hold be process A then these two processes are looked and cannot continue in their
processing.
http://www.jspm.com/tutorials/JavaConcurrency/article.html