Concurrency in Java - A Beginner's: What Are Threads
Concurrency in Java - A Beginner's: What Are Threads
introduction
Concurrency and parallelism features in Java are as old as Java itself. The original creators and
specification authors rightly presumed that programmers and future state software designs would
need high concurrency capabilities to scale their apps. The Java platform is designed from the
ground up to support concurrent programming, with basic concurrency support in the Java
programming language and the Java class libraries. Since version 5.0, the Java platform has also
included high-level concurrency APIs to ease the complex semantics of working with and
understanding Threading behaviors.
Sharing the address space between threads has a big advantage as it allows reuse of the same
data space between threads. If we have a separate small data stack, local to each thread, then data
sharing and data synchronization can become a big problem in programs. This is essentially the
same situations which we encounter related to Session state sharing across JVM clusters in a
production clustered environments. Distinct address spaces would require a complex mechanism
to share the mutable state across thread spaces as soon as the thread mutates its local address
space. In essence it would require address space synchronization mechanisms built into the
processor platforms that use Java.
So to prevent this problem threads dont hold their own state (except when you use
ThreadLocal) but instead rely on the programs address space for all their data input needs.
Though this solves the immediate problems of data synchronization across threads but this
creates another set of issues called race conditions which can make threads see stale data view of
a master address space. However this problem is much easier to solve by building thread safe
classes.
Since a single thread model access to an object is not a valid multithreaded model para-diagram,
hence no matter how well an object or a component set behaves in a single thread model setup, it
cannot be succinctly called thread-safe. Whether an object needs to be thread safe or not
depends on how that object is used instead of what it does. For some situations complete thread
safety may be a high requirement, but in some other cases a 95% to 98% thread safety coverage
may be sufficient.
Thread safety problem are always caused when two or more threads have mutable access to same
shared data set. When threads start reading and writing some shared variable without any control
then data based problems happen. Since threads share the same address space this is bound to
happen, especially when we have to rely on the Operating Systems capabilities and good faith
about when to swap the threads in and out. Application design should strive to build classes and
components to be thread safe from the very beginning. Age old concepts of encapsulation and
abstraction are our best friends here. Debugging and refactoring a poorly written component set
to be thread safe can be a herculean task and sometimes even a nightmare. So its important to
understand and employ correct thread safe class deign principles form the very start.
As long as the variable of state is not being modified e.g: a final int value, it is safe to
allow multiple threads to read it freely.
When an action involves a write operation to be executed, we get and hold a lock on the
variable and dont allow any read operations to pursue until the write is finished. This is
essentially what volatile does.
To make this strategy a flying success only thing that we need to do, as programmers, is make
sure that when write blocks the read operations, the read and write operations use the same lock
mechanism to test if the lock is free or not. If we can do this then we can get perfect thread
safety. This is exactly what the synchronized keyword or intrinsic locks (or monitor locks) do.
Though our programs would start behaving correctly in a multithreaded setup, however, this
approach has two big problems both of which convene to the same outcome.
Threads would block each other: When a write operation happens the read would block
since we have synchronized the write blocks. Then again when a read happens, the write
and other reads would also have to be blocked because in a multithreaded setup we really
dont know exactly when a read or a write would happen and thus we need to
synchronize the reads also to prevent them from being interleaving with writes. So in the
end we essentially are making the program single threaded in nature. Since only one
thread would always actually be alive doing the read/write, so even if we have hundreds
of threads they just wait out for a chance. Business money wise, we are not making an
effective use of those 8 CPUs with 4 cores each (that we have in production environment)
since at any time only 1 thread would be active when we could harness about 32 threads
in parallel. This is exactly the problem in any synchronized java class and in the
ConcurrentCollections of the java.util API.
Performance problems: With a multithreaded program in place its easy to expect it to
perform faster since we have made it multithreaded now. But because of the problem
explained in above point, code depicts no better behavior then it would in a single
threaded model. So blocking threads again convene our program to a single thread model
behavior.
To solve these problems we can make a few changes in our programming practice.Though we
would still synchronize on read and write operations, but his time we would intelligently keep
the blocking scope to the minimal in our code. This is to say that we dont synchronize the whole
method, just eh part of code in the method which actually mutates the shared data.
The following example code shown a typical use of synchronized thread safe idiom.
@ThreadSafe
1 public class EmployeeManager2 {
2 /*
3 * Mutating operations are encapsulated inside the class.
4 * State (employees) is not shared outside class directly.
*/
5 private List<Employee> employees;
6
7 public void addEmployee(Employee e){
8 //do some validations or preprocessing
9 synchronized(this){
//only this code blocks
10
this.employees.add(e);
11 }
12 //do somehting else
13 }
14
15 /*
* This is the right way to get data from collections.
16 * Finding by index is not safe as index may change over time.
17 */
18 public Employee getEmployee(String name)
19 throws CloneNotSupportedException{
20 //get the employee from the list
Employee employee = this.findEmployeeByName(name);
21 if(employee != null){
22 return (Employee)(employee.clone());
23 }else{
24 return null;
}
25 }
26
27 public void deleteEmployee(String name){
28 //get the employee from the list
29 synchronized(this){
30 for (Iterator<Employee> it = employees.iterator();
it.hasNext();) {
31 Employee employee = it.next();
32 if(employee.getName().equals(name)){
33 it.remove();//delet the employee
34 break;
}
35
}
36 }
37 }
38
private Employee findEmployeeByName(String name){
39 for (Iterator<Employee> it = employees.iterator();
40it.hasNext();) {
41 Employee employee = it.next();
42 if(employee.getName().equals(name)){
return employee;
43 }
44 }
45 return null;
46 }
47}
48
49
50
51
52
53
54
55
One way to do it is to have the individual components of the CTC package and tax formulas
within the employee object itself and when the employee is added to the list we do the
calculation in the synchronized block and populate the variables with the employee accordingly.
However this approach though still preserves the thread safety of the EmployeeManager but
creates a problem for us by expanding the scope of the synchronized block and thus the blocking
part of the add action and thus the performance of the manager.
Another approach would be to save the CTC and tax structure objects separately in another List
in the manager and ensure that the CTC and tax objects are navigable by the employee name/id.
This approach gives us a good performance heads up but then would again require us to
synchronize on the other two lists. This approach also gives us an added advantage of allowing
us to keep the employee add operation separate and mutually exclusive of the CTC package
add and tax structure add operations, thus allowing us a provision to induce parallelism
between the three.
A more preferred approach to do this, and essentially a variation of the second approach given
above, would be to keep the CTC and tax information within the employee itself, but to just
make their calculation actions parallel and mutually exclusive. Since we can retrieve the CTC
and tax operations are tied to an employee by his or her name/id so provided an employee id we
can calculate and mutate its CTC and tax structures in parallel while the manager serves its
clients. This approach is not only thread safe but also performance efficient.
However there is one small problem induced by our changing requirements (because our clients
think they are privileged few and can do anything they want). The client now says that unless the
tax structure is calculated and available for introspection, CTC should not be available for
viewing even if it has been calculated and vice versa. This essentially means that unless both the
operations of CTC calculation and tax structure calculation have finished successfully we cannot
disclose the CTC and tax information to the outside world i.e. outside the EmployeeManager.
What our client requirement mandates us to do is convert a singular parallel operation into an
atomic operation.
Atomicity
Atomic operations are a real world requirement and essentially a thread safety hazard because of
inherent thread nature to incline towards race conditions. Simplest of these problems, in an
increment or a decrement operation. E.g. a counter to keep track of number of requests served
per day in the web service or a counter to track the number of login failure attempts for a user. A
simple i++ operation is essentially composed of a set of 3 operations working together. Though
we dont see that in Java source but a disassemble operation can easily point out this
misconception. Allowing us the easy convention of ++ operator does not mean that java
internally also treats this as a single operation.
1
public class Temp {
2
3 public static void main(String[] args) {
4 int i=0;
5 i++;
6 System.out.println(i);
}
7}
8
And this is what the above code actually translates into. We can verify that using the javap
command tool with the disassemble flag.
Lets walk through this part to see how we would be harmed by the harmless code.
A. Assembly operation 0. Does the initialization of a variable and pushes the initial value of 10
into the variable. The bipush JVM instruction set takes a single 32 bit length (int) value and
pushes it on the operand stack.
B. Assembly operation 2. Pops the latest value in the operand stack and stores the value in a local
variable. The variable name is 1 and not i. Note that variable name in Java source code are just
human readable denominations and not actually the ones used by assembly JVM machine set. In
our example this instruction pops the value of 10 and stores in a memory address offset labeled
as 1 for it to read from later.
C. Assembly operation 3. Increments the variable. The iinc operator JVM machine set operator
takes 2 parameters.
First parameter is the name of the assembly level variable whose value is to be
incremented. In our case this variable name is 1, derived from B. above.
Second parameter is the value by which to increment which in our case is 1.
This operation saves the new value back in the variable after it increments (and is thus atomic in
nature in terms of increment and save).
D. Assembly operation 6 and 9. This is the call to our static construct System.out.println
followed by the call to load the assembly variable 1 back onto the operand stack so that the
System.out.println statement call can read it.
The line number table in the figure actually shows which line in Java code maps to which line in
disassembled code. Because of this problem the read, increment and get operations can
interleave over each other in a multithreaded setup thus giving wrong results. This read,
increment and get instruction set is actually a type of compound operation at a fine grained level
(byte code level) that needs to be made as a single atomic operation. Though in some cases this
may be acceptable as most software give a fault tolerant guarantee to about 98-99% of operations
but sometimes this may not be acceptable. Its far better to understand and fix such issues than
leave them to luck. It would be a nightmare if by chance you bought your favorite expensive
shopping item from an online checkout and invariably ended up paying for two or three pieces
when you only opted for one.
Race Conditions
Compound operations create a situation called race conditions in a multi threaded setup. Such
race conditions are not present in a single thread model but can only be seen when many threads
work concurrently. Race condition occurs when the correctness of the computation of a Thread
depends highly on its relative timing and its interleaving or scheduling characteristics relative to
other threads. We may get lucky 95% of times and the 5% of times when it fails would probably
be in a production environment.
Race conditions are not exactly the same as data races. Data races involve incorrect data access
because of wrong data/coding semantics most of the times. When the programmer forgets to use
proper synchronization blocks or leaks the state objects directly we end up with data races. Race
conditions on the other hand happen largely because of the context switching choices that the
underlying operating system or platform makes. Race conditions is essentially an undesirable by
product of Operating Systems going advanced and efficient by employing more finer level
parallelism at processor and multi core levels.
To solve our atomic increment problem in the previous section, we can employ the use of
AtomicInteger class and fall back on its getAndIncrement() method to safely increment our
variable without any further synchronization. AtomicInteger is one of the atomic operation
classes introduced in the new java.util.concurrent.atomic package. It uses CAS operation
semantics to achieve atomicity and durability in the increment operation. We shall discuss CAS
operations and instruction set when we talk through our advanced session on concurrency.
Compound Operations
We saw about one type of race condition in our previous section and also how to fix that problem
using an AtomicInteger. But what if we have two such counters or variables?
The ConnectionPoolWatchDog1 has 2 values to keep track of. The number of connections left
with it which it can give to its callers and the number of callers who have already borrowed a
connection. We could track both of these with a single variable but then what happens if a
connection is not borrowed and also not available for leasing out, probably because its
dependent socket is blocked or non-responsive. So its safe to use two counters to track this.
Now these two counters (borrowers and connections) need to be incremented and
decremented in one atomic operation. So we resort to an AtomicInteger here also. But then the
atomic variable is only mutually exclusive in context of one operation done on it (increment or
decrement). So even with using the atomic variables for both the counters we still cannot achieve
a thread safe setup because its possible that while one thread is on line 15 decrementing for a
borrow operation another thread may decide to return a connection and invoke the statement on
line 24.
It is important to note that lines 15 and 26 are mutually exclusive as they both operate on the
same variable (borrowers). So are lines 17 and 24. But together they are not, since they can
interleave with each other and possibility of a race condition (and even a data race) exists. So to
preserver the thread safety of this code we need to make sure that related variables are updated in
a single indivisible atomic operation.
Locking
Locking is a mechanism to explicitly and intentionally make a certain part of code single
threaded in nature even when it is run in a multi threaded setup. By employing locking we
intentionally restrict multiple threads from accessing some code flow concurrently thus
restricting the possibility of data mutation by multiple threads simultaneously. There are 2 types
of locking idioms in Java:
Intrinsic locks: These are also called monitor locks. This locking idiom relies on the JVM
guarantee that only 1 thread can ever own a lock on an object. While the object is locked
by a thread, all other interested threads wait for the lock to be opened. The se waiting
threads are not allowed to pursue some other work while they wait. Until the active
thread releases the lock all waiting threads suspend their working.
Extrinsic locks: These are custom made synchronizers which can be used to influence the
locking behavior in code. Java offers a custom java.util.concurrent.locks.Lock
interface to implement more extensive locking operations. Extrinsic locking idioms allow
more fine grained control over the operations, as against intrinsic locks, since the
operation can be spread across and performed until the unlock action is called on the
associated Lock object. Extrinsic locks are discussed in more detail in the advanced
session.
Intrinsic locks are essentially implemented in java using the synchronized keyword. When used
in the method signature it synchronizes the whole method against the lock of the this object.
When used as a block it synchronized the block code against the lock of the object passed to it as
a parameter. ConnectionPoolWatchDog2 solves its thread safety problem by using a
synchronized block and locking on the this instance as shown below.
1 @ThreadSafe
2 public class ConnectionPoolWatchDog2 {
//we increment this counter
3 @GuardedBy("this") private AtomicInteger borrowers;
4
5 //we decrement this counter
6 @GuardedBy("this") private AtomicInteger connections;
7
8 public ConnectionPoolWatchDog2() {
9 this.borrowers = new AtomicInteger(1);
this.connections = new AtomicInteger(10);
10 }
11
12 public void borrowOne() {
13 //do some tasks
14 synchronized(this){
15 //increment the borrowers count
this.borrowers.getAndIncrement();
16 //decrement the remaining conenction count
17 this.connections.getAndDecrement();
18 }
19 //do some other tasks
}
20
21 public void returnOne(){
22 //do some tasks
23 synchronized(this){
24 //increment the remaining conenction count
25 this.connections.getAndIncrement();
//decrement the borrowers count
26 this.borrowers.getAndDecrement();
27 }
28 //do some other tasks
29 }
30}
31
32
33
34
35
Because the currently active Thread locks out other Threads from gaining access on the object on
which synchronized is called out, we say that the thread has locked the object and not the other
way round. It just the same corollary of booking a hotel room for a night. We book the hotel
room and while we stay in that hotel room other customers have to wait it out till we relinquish
the lock on the hotel room. A hotel room is like a code block that is synchronized. Only one
customer (Thread) can stay in it (run through it) at a given time. We book the room with the
hotel, we pay the hotel for the room not the other way round, just as we lock the object for access
to synchronized block. When we are done with the room, we relinquish its lock (exit the
synchronized block) and its then that others can see the state of the room and if needed book it.
It is important to understand that while a thread holds the lock, other threads waiting for that lock
dont see any changes made to the state by the active thread. This is true even if the active thread
is more than the half way through with the synchronized block or even if its on the last
executable statement in the synchronized block or method. Its only when the active thread exits
the synchronized block or method, that the waiting threads come alive and see what changes
actually happened.
However this is not true for threads waiting on different locks. If a Thread T1 is locked on Obj1
and is making changes to a List employees, threads that are blocking on the lock of Obj1 will
not be able to see these changes until the thread T1 exits the lock. However for a Thread T2
locked on some other Obj2 or not locked on anything, can still see these changes as they happen.
So while T1 is executing the employees list mutation changes inside the synchronized block T2
can see them all as they happen because its not blocked on the lock which T1 has acquired and if
free to do what it wants. So if a synchronized block is needed to guard access to a variable or
state, then it is important to ensure that all operations to that state everywhere in code must be
guarded on the same lock provider or object. When a class has invariants that involves a tight
coupling between more than one variable or state holder it is important to ensure that locks are
owned consistently on the same object everywhere. Without this thread safety can be
compromised.
Reentrancy
Reentrancy is actually an Operating System concept of making the processes and process shared
stack reentrant to different processes for effective and block free IPC. Java also borrows this
technique from there and implements this on finer grains object intrinsic lock mechanism.
When a thread obtains a lock on an object all other threads requesting that lock wait or
essentially block on the object. When the original thread releases the lock other threads can
acquire the lock again. This is because intrinsic locks are reentrant. Thus intrinsic locks are by
nature acquired on a per thread basis rather than per request basis. This gives rise to a concept of
reentrant synchronization. Because a thread can acquire a lock it already owns we call it
reentrant synchronization. Without this a Thread would deadlock on itself.
1
2 public class Temp2 {
3 private int age = 10;
4 private synchronized void hello(String name, int age){
String temp = name + " there";
5 synchronized(this){
6 this.age = age;
7 }
8 System.out.println(temp + " age " + age);
9 }
10
public static void main(String[] args){
11 Temp2 t2 = new Temp2();
12 t2.hello("user", 20);
13 }
14}
15
The above code demonstrated the concept of reentrant synchronization. Below figure is the
disassembled version of above code.
Lets walk through the sample output of disassembled code.
A. Shows the ACC_SYNCHRONIZED flag. The JVM automatically enters the monitor on the this
object when it encounters this flag.
B. This is a monitorenter instruction set. It enters the monitor on the variable popped at point
C. in the diagram which happens to be our object. The LocalVariableTable shows the java object
ref/variable mapping with the assembly variable mapping.
D. Shows the monitorexit instruction. After this instruction the monitor is again available for
entry by any other thread.
Reentrancy facilitates the java OOP principle of encapsulation but at the locking mechanism
level. Without reentrant behaviors threads can deadlock as the waiting threads would never get
the change to own the lock once its already taken.
Essentially this is just a more technical wording for a plain simple common sense operation. If
you write something to a variable and read it afterwards then presuming no other write operation
happened or is in progress you should see the same value in the variable which was earlier
written out. Though this seems commonsense enough but its true only in single threaded model.
In multithreaded setup without synchronization there can be many variations that can make it
totally untrue. So locking is not just about making a group of actions atomic with respect to
thread interaction (mutual exclusion) but also about ensuring that threads (other waiting threads
and even itself) see the most up to date state of shared data (visibility).
Java Memory Model offers a happens before relationship guarantee which ensures that state
changes happen correctly and new changes overwrite the old ones consistently and in an
idempotent manner. Java Memory Model however stresses on two important state guarantees
which the class designers must understand.
From a JVM implementation perspective (and not a Java Programs perspective) the
specification states that every field (a memory address and its associated offset), including array
elements placements, are distinct and mutually exclusive. This means that any update operation
to a field address is mutually exclusive to any other update operation that precedes or succeeds
this operation. Two threads that update adjacent elements of an array separately must not
interfere or interact and do not need synchronization to ensure sequential consistency. In essence
every field operation is atomic in itself.
However some processor architectures dont provide a single atomic machine set instruction to
write to a single byte if the variable length is more than that. On such platforms an update to a
byte array is actually a compound operation involving:
This operation is illegal as per JVM specs because in a multithreaded setup where
unsynchronized code is updating the array in 2 threads, one thread may see stale array state while
another thread is updating it. This is called as word tearing. Most of the 32 bit processor
platforms suffer from this anomaly though not all. All java types which require at the most 32
bits of storage space for correctly holding their values san be safe written and read in one atomic
operation. However types with length more than 32 bits e.g. Long and Double and array types
which are of 64 bit length, are not safe as their write operations constitute two 32 bit writes in
quick succession. JVM specs calls this as non-atomic writes. Though most of modern 64-bit
platforms dont suffer from this problem but still JVM specs tries to be on the safe side here and
explicitly states that:
Writes to and reads of references are always atomic, regardless of whether they are
implemented as 32-bit or 64-bit values.
Writes to and reads of primitives with size up to 32-bits is atomic.
Writes to and reads of primitives and arrays with size more than 32-bits is non-atomic
unless they are explicitly stated as volatile.
Volatile Modifier
Volatile is an alternate weaker form of synchronization that Java provides to supplement intrinsic
locking. While intrinsic locking works and is used at a macro level in the Java programming
model i.e. in high level Java source code, volatile performs the same function partially at a micro
level i.e. at the processor or machine set execution level. Thats why volatile is not used with
methods or classes but only with variables or fields.
When a field is declared as volatile, the JVM and runtime are directed that:
The said variable is a shared variable and most likely would be involved in a multi-
threaded setup.
The said variable should not be cached in any processor L1 or L2 caches or local registers
but instead always re-read directly from the memory location where it is saved.
The JVM and runtime should not reorder instruction execution of statements around the
volatile variable.
The current thread checks for the variable isReady and if not true goes to sleep for 1 sec and
retries again. Though this would work perfectly on a single processor platform (even with
multiple threads in code) but on a multi-processor platform with multiple threads running in code
the call to t3.isReady at A. can actually be cached in processor local registers. So if another
thread updated isReady field the waiting thread may not know and could loop forever since it
prefers the view of the variable value in processors instead of whats there in memory. Making
the variable volatile solves this problem. Thus volatile modifier ensures visibility guarantees for
the variable on which it is declared.
Volatile ensures that all competing threads see the most up to date value of the variable that was
last written to it. If a thread wants to read the variable but it is being written at the moment the
thread waits and once the write finishes the read proceeds to read the latest value. If the writing
thread is queued up after read, then the read proceeds and see the value that is up to date at the
moment. In this essence writes to a volatile variable mimic a monitor exit action (exiting a
synchronized block) and reads to a volatile variable mimic a monitor entry action (entering a
synchronized block). Thats why sometimes volatile is called as half synchronization. This
nature of volatile makes it an excellent tool to prevent word tearing problems. Since reads would
always see the most updated writes so volatile doubles and longs are safe to be used in multi-
threaded setups even on 32 bit platforms because the JVM runtime would not move ahead until
the whole word is read in or modified in one go.
Constructing thread safe classes and components involves two things to know and care about:
Design principles: These principles relate about what you should and should not do while
designing, coding and refactoring thread safe classes or components. At a high level these
principles can be divided into instance confinement strategies and thread confinement
design principles.
Documentation principles: These principles advise to be explicitly clear and forth coming
about the synchronization policies of the class or component. Typically these principles
advise us to clearly answer questions like What the class is intended to do? How does it
do it ? What considerations and traps should its clients or callers be aware of? or the
synchronization policies employed by the class
When designing thread safe classes it is always best to not rely on clients or callers of you
classes and objects to provide synchronized access to state mutation operations. Because the
clients may not be fully aware of what state mutates and when they may eventually wrongly
synchronize their calling code which can impact thread safety of overall application. This is a
common problem in the Synchronized Collections API which are created using the
Collections.synchronized API call. The collections returned by Collections.synchronized
API calls actually use the decorator pattern to add synchronization on top of existing Collections
constructs making the existing collection operations atomic. This means that writes and reads are
atomic within themselves. However for client programs which use these collections there are
always compound invariants which involve a read, mutate and write operation on these
collections which also should be atomic. So the following code may seem harmless in a single
threaded setup but would fail in a multi threaded model.
To fix these problems we need to synchronize the EmployeeManager to allow only 1 operation at
a time. How bout this?
Why?
Because this class synchronizes on this object where as it should synchronize on employees.
This is what the Javadocs for Collections.synchronizedMap state.
This means that internally in the employees the synchronization is based on the lock of the
underlying map. But in our client class EmployeeManager it is based on the instance of
EmployeeManager represented by this. Clearly these are two different objects and thus threads
willing for locks on these objects wont block each other. Also if employees is accessed from a
subclass of EmployeeManager then synchronized on this wont help in blocking the access
either. Its the map operations which we need to make atomic not our own code. So we must
synchronize on the employees variable instead of this.
EmployeeManager5 is a good example of an instance confined thread safe class (though not
100%).
Instance confinement is used heavily in the Collections API to make synchronized counter parts
of normal Collections like HashMap and Lists. They actually make use of a decorator pattern to
create a synchronized version of the collection and keep the synchronized version classes as
private static inner classes so that these class instances cant escape. Instance confinement makes
it easy to construct thread safe classes as the only class to be analyzed for thread safety is the
owner class. Without this analyzing thread safety in a big project can become a nightmare.
Delegation
Composition
Private Monitor Pattern
Using this principle our EmployeeManager5 could be better off to use a ConcurrenHashMap
instead of a synchronizedMap and delegate all its check if it contains and then mutate
operations to atomic putIfAbsent. Also the get operation on ConcurrenHashMap is atomic
about contains and get mechanism. So by using a ConcurrenHashMap we can eliminate the
need for contains check in our EmployeeManager5 code.
Using delegation also has another advantage. It eliminates the need for locking in our code.
Since in the above example we rely on ConcurrenHashMap now so we can eliminate the
synchronized keyword and locking idioms from our code and thus make it lock free with respect
to our instance scope. This also eliminates the issue of thread blocking on each other due to
synchronized block locks.
Delegating thread safety works when we dont have dependent shared mutable state variables to
take care of. E.g. ConnectionPoolWatchDog2 (see previous code example in Locking section)
cannot be made thread safe by delegation alone. Since it involves two shared invariants that are
dependent on each other, so some form of client side locking is essential though the two
invariant types are themselves thread safe.
Lets presume we want to add a new behavior getIfPresent to a HashMap. There are two
ways to do this.
Approach 1 Inheritance
Create a new interface type CustomMap which extends Map which adds the new
behavior getIfPresent
Extend HashMap to make a CustomHashMap which implements all the same interfaces
which a HashMap except Map. Instead of Map interface we implement the CustomMap.
Add the method implementation for the new behavior.
Approach 2 Composition
Create a new interface type CustomMap which extends Map which adds the new
behavior getIfPresent
We create a private instance variable of type HashMap in our CustomMap instead of
extending HashMap.
We implement the new behavior getIfPresent in our CustomhashMap class.
But for all existing Map methods or actions we just delegate to the private HashMap
instance.
Sometimes again grouping such objects types using inheritance may not actually be
abiding to the OOP principles. E.g. a car may need a behavior replaceIfCustomerLikes
for its seats and upholstery. Adding this behavior to Automobile super type using
inheritance may not be a good idea as such a behavior is not a generic one which an
automobile exhibits. Its an invariant dependent on the owner of a car.
Now extend this principle to an java.lang.Object instance stored in the class, as instance
variable, that is intended to be used as the exclusive lock provider for all operations on the
instance. Now if we dont allow a reference to this to escape during instance construction, we
achieve a perfectly thread safe class. This is shown in the following code example.
1 @ThreadSafe
2 public class EmployeeManager6 {
private List<Employee> emplyees;
3 private final Object lockMonitor = new Object();
4
5 public EmployeeManager6(){
6 this.emplyees = new ArrayList<Employee>();
7 }
8
9 public Employee getEmployee(int index)
throws CloneNotSupportedException {
10 synchronized(this.lockMonitor){
11 return (Employee)(this.emplyees.get(index)).clone();
12 }
13 }
14
public void addEmployee(Employee employee) {
15 synchronized(this.lockMonitor){
16 this.emplyees.add(employee);
17 }
18 }
19
20 public void deleteEmployee(int employeeId) {
21 synchronized(this.lockMonitor){
for (Iterator<Employee> it = emplyees.iterator();
22it.hasNext();) {
23 Employee emp = it.next();
24 if(employeeId == emp.getEmployeeId()){
25 it.remove();
break;
26 }
27 }
28 }
29 }
30
31 public void deleteEmployee(Employee employee) {
synchronized(this.lockMonitor){
32 this.emplyees.remove(employee);
33 }
34 }
35
36 /*
* returns an unmodifiable list.
37 * Can also return a copy of the original list.
38 *
39 */
40 public List<Employee> getAllEmployees(){
41 return Collections.unmodifiableList(this.emplyees);
}
42}
43
44
45
46
47
48
49
Since all the methods bodies that need to maintain exclusive thread access use only the
lockMonitor object, so no two synchronized blocks can ever race against each other. Also the
monitor is declared as final so the the reference lockMonitor does not accidently change its
target object. As we shall see in the section about final modifier, such an action can have very
wrong impact on an otherwise perfectly thread safe class. Further more since the lockMonitor
object is not exposed to outside world no one can accidently modify it or alter it. Client of
EmployeeManager6 dont need to take any additional synchronization steps as long as they dont
try to derive operations new operations by combining existing operations of EmployeeManager6
. Responsibility of such new operations should be shouldered by the EmployeeManager6 class.
The following code block shows an attempt by a client to create a compound operation out of
methods exposed by EmployeeManager6 .
The operation deleteEmployeeRecord is not thread safe, though its two invariant operations
themselves are. This is because there is a margin of possibility for race situation when the code
flow make a transition between the calls of getEmployee and deleteEmployee on
EmployeeManager6 instance as shown below by the disassembled code below.
But the worst part is that EmployeeManager6Client cant do anything about it. If it would
attempt to synchronize the deleteEmployeeRecord method on its own it would fail equally
miserably. Why?
This is because the invariant operations getEmployee and deleteEmployee are actually
synchronized on the lock which is provided by the lockMonitor object and this lockMonitor
object is fully encapsulated inside the EmployeeManager6 class instance. So believing that it
would be a bad practice to add a getter for lockMonitor in EmployeeManager6 class, the only
option left for us to follow is the right choice here, and that is to let EmployeeManager6 should
the responsibility to implement this method inside it and expose the implementation in its public
contract.
Thread Confinement
Sharing mutable data requires correct synchronization mechanisms, failing which the results can
be dangerous. One way to avoid this is to not share the mutable data directly. If we can ensure
that only one thread ever accesses the mutable state then we dont need synchronization at all to
manage the data mutation problems. This philosophy is called as thread confinement. There are a
couple of ways to do this as subsequent sections explain.
Another thing to note here is that the caller of this method passes the employee variable by
reference, so it still has a mutating handle on the employee. If we dont create a local variable
out of this employee , there can be a situation where the check on containsKey passes, but at
that very moment the caller of this method changes the employeeId of the associated
corresponding employee object. Its always a good practice to make the method parameter
references final and make a copy of mutable parameters before actually proceeding to work with
them.
/*
01 * This is the right way to get data from collections.
02 * Finding by index is not safe as index may change over time.
03 */
04 public Employee getEmployee(String name)
throws CloneNotSupportedException{
05 //get the employee from the list
06 Employee employee = this.findEmployeeByName(name);
07 if(employee != null){
return (Employee)(employee.clone());
08 }else{
09 return null;
10 }
11 }
12
13
14
The example code here gives an effective example of creating a safe working copy. The method
returns a working copy of the employee which it fetches form an internal list. The clients of this
method use the working copy of this employee object instead of a direct reference to the
employee which his in the list. Even if the clients change the returned employee object, the
original employee in the list is not modified and thus the invariant state is not altered. It is
important to note that the Employee object returned by the method is not immutable in nature.
Clients can still make changes to the same object. Its just that those changes dont find their way
back in the list unless someone tried to update the employee again.
Another common use of TheadLocal was in HibernateUtils during the times of Hibernate 2 days.
To make use of Hibernate then you were required to created a Utility class that would hold the
Hibernate session factory and return newly created session objects to its callers. Part of this
utility is shown below which illustrates the use of TheadLocal .
One common pattern that we can see in classes that make use of TheadLocal effectively,
is that the classes themselves are not interested in the TheadLocal data.
They are actually interested in sharing that data. E.g. RandomNumber generator doesnt
consume the number itself. It gives those numbers to its callers to use. Same is the case
for HibernateUtils too. It just creates the session and gives it to its callers. Similar
structure is also prevalent in connection pool designs.
So thread locals make effective sense only in such situations
Another issue which TheadLocals have is related to application relying on heavy Thread
Pooling. TheadLocal can be very dangerous when it comes to long running applications
and garbage collections especially in server applications.
After reading the TheadLocal API and Javadocs it can be assessed that the object put in a
TheadLocal context is associated to a particular thread, and will be garbage collected
once the containing thread is dead.
So there is a high risk that the object stored in the thread local may never be garbage
collected when your app runs on a server like Tomcat or WebSphere, which their own
pool of working thread, even when the class that created this TheadLocal instance is
garbage collected. This can lead to memory leaks and connection leaks.
This is the exact problem that happens in Tomcat v7 when we use custom connection
pools which create their own thread locals but rely on tomcat for their thread pool
management requirements.
Tomcat v7 displays a following message (or similar one) in its logs when such a webapp
is undeployed, redeployed or shutdown. This message is for the MySQL driver
ThreadLocal leak.
This post doesnt go into details of creating an immutable object as this is a vast topic in itself.
However a brief guideline is states about the behavior of immutable objects. An object is deemed
immutable if and only if:
It does not allow any public access to mutate its internal state.
It does not allow state mutation using cloning and reverse cloning.
It does not allow state mutation using reflections API.
All its state data is preferable tagged with final modifier.
It does not allow a reference to this to escape during construction
Final modifier on variables plays a fundamental role in ensuring immutable state transition and
also in JVM runtime performance optimizations. Please note that this is not the same as final
modifier on classes and method signatures. While objects can be made immutable as outlined
earlier but still their references are not immutable in nature.
1
2 public class Temp6 {
3 private String name;
4
public String getName() {
5
synchronized(this.name){
6 return name;
7 }
8 }
9
10 public void setName(String name) {
synchronized(this.name){
11 this.name = name;
12 }
13 }
14 }
15
Though name is a String and is immutable in above code and lets say constitutes the shared data
so we synchronize the getter and setter on it. But the String object itself is immutable, this we
know, however the reference name is not. This means we can construct another String object
somewhere in our program and assign the reference name to it and nothing would go wrong, at
least programmatically. This is essentially what the setter does here. But if the object to which
the reference name changes then our synchronized locks fail.
Why?
Because the synchronized(name) code obtains the lock on the String object to which the
reference name points to and not on the reference itself. When a thread T1 calls
synchronized(name) at line 11, it obtains a lock on the String object to which the reference
name points to. But then T1 changes the object pointed to by name on line 12. At that point of
time any other thread T2 or T3 can work through both the methods (get and set) since the object
on which they would obtain a lock would most probably not be the same as the one on which T1
did.
To make Temp6 thread safe we need to declare name as final and provide capability methods or
a factory to make new Temp6 objects as the state of name variable changes. That way the
reference cannot change again. In essence we need to make Temp6 immutable and declare the
reference name as final.
The EmployeeManager5 class which we discussed earlier also suffers form this problem. Since
the synchronized block in the class, synchronizes on the reference employees which is not
final, changing the object to which employees points can break the thread safety of our class.
Because of these reference swapping restriction rules on Final variables we get what is called as
Initialization safety for such variables and their use across code. This allows JVM
implementations to work up some performance optimizations of their own too. Because of the
initialization guarantees for their variables JVM runtimes are free to cache final variables in
processor local L1 and L2 caches. Since these references cannot be shifter from one object to
another, such objects can be cached for quick access with minimal loss to durability.
Guarded By Lock
Most of the design principles outlined so far, work well enough to ensure thread safety of classes
and components. However in some situations these alone may not be enough. In such situations
it becomes a necessity to include some locking mechanism, either intrinsic or extrinsic in
addition to the above techniques.
Though locking provides a fool proof mechanism to ensure thread safety but locking idioms are
essentially blocking in nature. By using locking idioms we intentionally enforces sequential
behavior over the code block that it guards even if there are multiple threads waiting for access.
This is analogous somewhat to what pipes do in Operating System designs. An immediate
impact of this is on the performance of the overall program but keeping lock contention scopes
as minimal as possible, performance bottlenecks can be reduced though never totally eliminated.
All of the other techniques outlined earlier are non-blocking in nature and thus provide much
better performance than intentional locking mechanisms.
Documentation Principles
As much as it is difficult and important to write effective concurrent code, its equally difficult
and far more important to document the behavior of your concurrent code correctly. Most of the
time incorrect multi-threaded code results from incorrect use of library APIs and frameworks.
The connection pools that we used in our projects does provide us a thread safe
connection object or not OR
Our own connection factory for pooling the JCR session object provides thread safe
access to JCR session objects for use in our CQ projects OR
Hibernate provides us a Thread safe access to session object.
I know point 3 is true but I dont know about point 1 and 2. This is because the framework
owners or creators didnt document this aspect as effectively as the Hibernate creators did.
Hibernate Javadoc API clearly states this out and any deviations naturally mean a bug in
Hibernate code. So what about point 1 and point 2? If my connection pool does not behave as
expected is that a bug in my code or in the framework.
Honestly there is no end to this argument once it starts. The point to understand here is that
concurrency policies and behaviors for classes should be documented explicitly and clearly. We
need to call out if the clients of our class need to employ synchronization to achieve thread safety
or do we handle it effective in our framework code.
Java Concurrency - Part 1 : Threads
The concurrency is the fact to made several things at the same time using several threads.
A thread, also called Lightweight Process, is treatment unity. Threads executes code in parallel
of each other threads currently running. When you've only one processor, there is a thread
running at the same time of the others, you only have the impression of concurrency (I don't say
it's not useful, I say it's different), but when you've multiple processors, you'll see the power of
multithreading. In this case, you can have your threads distributed on the processors of the
computer.
In Java, a thread is an instance of the class java.lang.Thread. A Thread can be managed in one of
these two ways :
1. Directly mapped to a native thread of the operating system. This is used when the
operating system provide a preemptive threading system.
2. Managed by the virtual machine in a preemptive way.
A preemptive system, is a system in which the threads are managed by a scheduler and can be
interrupted at any time to give processor to an other thread. When you program, you doesn't have
to pay attention of which type of threads you use, the result will normally be the same. But
you've to know that there can differences between operating systems.
We'll see these concepts more deeply in the others parts of the set.
Lets start introducing the Thread class in Java. You can create threads in two ways :
1. Extends Thread
2. Implements Runnable and pass an instance of your news class to the Thread constructor
The first solution isn't a good solution because what you're creating is not a new specialized
thread, but several instructions to run in a new Thread, namely a Runnable. Implementing
Runnable is also better because Runnable is an interface and so, you can also extends a class
and implementing Runnable, that's useful in some cases.
In my examples, I'll always use the second way. So let's declare our first Runnable :
The Thread will stopped when the end of the run() will be reached. You cannot force a thread to
stop (there is stop() method, but deprecated), we'll see later how to properly stop a thread.
And now, what happens if we add a simple line of code to our program :
Can you predict the result of this code ? Nobody can't, it's not predictable, you can have :
In a thread
In the main Thread
or
This is the best example to see that the other is unpredictable. Here are two executions on my
machine :
1.
2.
Like you can see, the order the threads instructions are executed is not guaranteed at all.
First and easier, we can make a thread sleeping for a certain number of milliseconds. To do that,
the Thread class has a method sleep(long millis). But this method is static, so you can only make
the current Thread sleeping. You cannot choose the thread you want to make sleeping, your only
choice is the current Thread so :
Thread.sleep(1000);
makes the current Thread sleep during 1000 milliseconds (1 second). But, you have to catch an
exception, InterruptedException. This exception occurs if the sleeping thread is interrupted. So
you have to do that :
try {
Thread.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();
}
But this not the good way to manage the InterruptedException. We'll see in one moment, how to
deal with this exception.
If you want more precision, you can use the overloaded version of sleep that takes the number of
milliseconds plus a certain number of nanoseconds to sleep. The precision of this sleep depends
on the system timers and clocks.
For example, if you want to sleep 1000 milliseconds and 1000 nanoseconds (1 microsecond),
you can do like that :
try {
Thread.sleep(1000, 1000);
} catch (InterruptedException e){
e.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(2, 5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
You can see that the sleep of milliseconds is very precise, but with nanoseconds the result can
vary a lot. And of course, the result depends of your computer, your operating system and your
configuration.
An other thing, you can do with Threads, is waiting for an other thread to die. For example, you
can create five thread to compute sub result and wait for these 5 threads to finish to compute the
final results based on the results of the five threads. To do that, you can use the join() method of
the Thread class. This method is not static, so you can use it on any thread to wait for it to die.
Like sleep() this method throws InterruptedException in the when the thread is interrupted
during waiting for an other thread. So to wait on thread2, you just have to do that :
try {
thread2.join();
} catch (InterruptedException e){
e.printStackTrace();
}
That will make the current Thread waiting for thread2 to die. You can also add a timeout in
millis, or millis + nanos, with the overloaded versions of join(), join(long millis) and join(long
millis, int nanos). Here is little example that shows all that stuff :
thread2.start();
try {
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
thread3.start();
try {
thread3.join(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
You can see that the first join() wait 5 seconds for the other thread and when we set a timeout,
we wait only 1 seconds and return from join method.
When working with Threads, it's also possible to change the priority of a Thread. In the Java
Virtual Machine, the Thread scheduler, use a priority-based scheduling. So if a Thread enter in
Runnable state with a higher priority than the running Thread, the new Thread will run and the
current running thread will return to Runnable state and waits for its turn. But this behavior is not
guaranteed and is completely depending on the virtual machine you are working on. So, do not
rely on thread priorities, just use them to improve efficiency of your program.
Normally, the priority range of Threads is an integer from 0 to 10, but some virtual machine have
lower or higher ranges. To know the range of priority, you can use constants of the Thread class :
Minimal priority : 1
Maximal priority : 10
Norm priority
To set the priority of a Thread, you can use the setPriority(int priority) method of the Thread
class. If you enter a value greater than the maximal priority, the maximal value will be used. If
you don't specify a priority, the used priority, will be the priority of the current Thread.
An other way to works with priority is the yield() method. This method is static, so this works on
the current Thread. The purpose of this method is to make the Thread going to Runnable again
and to give the opportunity to other threads to get their turn. But in practice, the behavior of this
method is not guaranteed. It can be implemented as a no-op on certain systems. It's not easy to
test that in practice, because the results can truly depends on your computer, virtual machine and
operating system. It's a good things to not use the priorities of Threads in practice.
The last thing you can do with a Thread, is to interrupt it. In Java, you have no way to force a
Thread to stop, if the Thread is not well-done, it can continue its execution infinitely. But you
can interrupt a Thread with the interrupt() method. This method interrupt the thread, if the thread
is sleeping or joining an other Thread, an InterruptedException is thrown. You have to know that
if the thread was sleeping or joining, the interrupted status of the Thread will be cleared. Namely,
the method isInterrupted() will return false. A little example to demonstrate that :
thread1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread1.interrupt();
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("The thread has been interrupted");
System.out.println("The thread is interrupted : " +
Thread.currentThread().isInterrupted());
}
You can see that after one second, the second thread is interrupted and that the interrupted status
has been set to false. If you are not sleeping, but making a lot of heavy actions, you can test for
interrupt like that to make your thread correctly interrupts :
Now that you know how to interrupt a thread, you can imagine, that simply catch the
InterruptedException is not enough to make your thread "interrupt safe". Imagine that your
thread something like that :
And now, an other thread want to interrupt your thread while your thread is sleeping. The sleep
will be interrupted, but the interrupted status will be cleared so the loop will continue. A solution
to make a better thread is to interrupt again the thread after an InterruptedException :
With that code, the interrupted status will be restored and the loop will be stopped after interrupt.
Depending on your code, you can also add a continue statement after the interrupt() to not make
operations after interrupt. In some cases, you'll also needs to make several if statements to test
the interrupted status to do or not to do some operations.
Synchronization is a way to make some code thread safe. A code that can be accessed by
multiple threads must be made thread safe. Thread Safe describe some code that can be called
from multiple threads without corrupting the state of the object or simply doing the thing the
code must do in right order.
It's really simple and works well with one thread, but absolutely not with multiple threads. An
increment like this is not a simple action, but three actions :
Normally, if you have two threads invoking the getNextValue(), you can think that the first will
get 1 and the next will get 2, but it is possible that the two threads get the value 1. Imagine this
situation :
These situations come from what we call interleaving. Interleaving describe the possible
situations of several threads executing some statements. Only for three operations and two
threads, there is a lot of possible interleavings.
So we must made the operations atomic to works with multiple threads. In Java, the first way to
make that is to use a lock. All Java objects contains an intrinsic locks, we'll use that lock to make
methods or statement atomic. When a thread has a lock, no other thread can acquire it and must
wait for the first thread to release the lock. To acquire the lock, you have to use the synchronized
keyword to automatically acquire and release a lock for a code. You can add the synchronized
keyword to a method to acquire the lock before invoking the method and release it after the
method execution. You can refactor the getNextValue() method using the synchronized keyword
:
With that, you have the guarantee that only thread can execute the method at the same time. The
used lock is the intrinsic lock of the instance. If the method is static, the used lock is the Class
object of Example. If you have two methods with the synchronized keyword, only one method of
the two will be executed at the same time because the same lock is used for the two methods.
You can also write it using a synchronized block :
This is exactly the same as using the synchronized keyword on the method signature. Using
synchronized blocks, you can choose the lock to block on. By example, if you don't want to use
the intrinsic lock of the current object but an other object, you can use an other object just as a
lock :
The result is the same but has one difference, the lock is internal to the object so no other code
can use the lock. With complex classes, it not rare to use several locks to provide thread safety
on the class.
There is an other issue with multiple threads : the visibility of the variables. This seems when a
change made by a thread is visible by an other thread. For performance improvements, the Java
compiler and virtual machines can made some improvements using registers and cache. By
default, you have no guarantee that a change made by a thread is visible to an other thread. To
make a change visible to an other thread, you must use synchronized blocks to ensure visibility
of the change. You must use synchronized blocks for the read and for the write of the shared
values. You must make that for every read/write of a value shared between multiple threads.
You can also use the volatile keyword on the field to ensure the visibility of read/write between
multiple threads. The volatile keyword ensure only visibility, not atomicity. The synchronized
blocks ensure visibility and atomicity. So you can use the volatile keyword on fields that doesn't
need atomicity (if you make only read and write to the field without depending on the current
value of the field by example).
You can also note that this simple example can be solved using AtomicInteger, but that will be
covered later in an other part of the posts.
Pay attention that trying to solve thread safety on a problem can add new issues of deadlock. By
example, if thread A owns the lock 1 and are waiting for the lock 2 and if lock 2 is acquired by
thread B who waits on lock 1, there is a deadlock. Your program is dead. So you have to pay
great attention to the locks.
There is several rules that we must keep in mind when using locks :
1. Every mutable fields shared between multiple threads must be guarded with a lock or
made volatile, if you only need visibility
2. Synchronize only the operations that must synchronized, this improve the performances.
But don't synchronize too few operations. Try to keep the lock only for short operations.
3. Always know which locks are acquired and when there are acquired and by which thread
4. An immutable object is always thread safe
Here we are, I hope that this post helps you to understand thread safety and how to achieve it
using intrinsic locks. In the next posts, we'll see another synchronization methods.
Semaphores are a really simple concept, invented by the famous Dutch computer scientist Edsger
Dijkstra. Basically a semaphore is a counter (integer) that allows a thread to get into a critical
region if the value of the counter is greater than 0. If it's the case, the counter is decremented by
one otherwise, the thread is waiting until it can go. And when the thread go away from the
critical region, the counter is incremented by one to allow one more thread to pass the critical
region. A semaphore is created with a certain value for its counter. So, you can execute two
actions on a semaphore P and V.
By example, if you have a critical that cannot be executed concurrently, you can use a
semaphore:
So you must always call by yourself the P operation before the critical region and V after it. We
call a mutex (mutual exclusion) a semaphore with a value of one. So only one thread can enter
the region guarded by the semaphore. This is the most used semaphore. The other use of
semaphore is to guard a set of resources like database connections or a data pool.
In Java, a semaphore is created using the java.util.concurrent.Semaphore class. You can create
easily :
The P and V operations are represented using the acquire and release methods. The method
acquire can be interrupted if the thread is interrupted. There is an uninterruptible version with the
method acquireUninterruptibly(). There is also a third version with the tryAcquire method. This
method acquire a permit only if there is one permit available, otherwise, this method return false
directly. All the waiting methods have also an overloaded version with a timeout. You can also
acquire several permits at once using the permits argument to the different versions of acquire
methods.
A little example with a mutex using the same example as the previous post on Java concurrency :
For more informations about Semaphore in Java, the best is to consult the Javadoc of the
Semaphore class.
To conclude, semaphores are a powerful ways to solve concurrency problems, but this is not
adapted to all problems. If you need only mutual exclusion, synchronized blocks are a better
solutions. The problems with semaphores is that you can forget to call the release method and
that can cause deadlock sometimes difficult to find.
Monitors are an other mechanism of concurrent programming. It's a higher level mechanism than
semaphores and also more powerful. A monitor is an instance of a class that can be used safely
by several threads. All the methods of a monitor are executed with mutual exclusion. So at most
one thread can execute a method of the monitor at the same time. This mutual exclusion policy
makes easier to work with monitor and to develop the method content of the monitor.
Monitors have an other feature, the possibility to make a thread waiting for a condition. During
the wait time, the thread temporarily gives up its exclusive access and must reacquire it after the
condition has been met. You can also signal one or more threads that a condition has been met.
All the synchronization code is centralized in one location and the users of this code
dont need to know how its implemented.
The code doesn't depend on the number of processes, it works for as many processes as
you want
You dont need to release something like a mutex, so you cannot forget to do it
When we must describe a monitor, we simple use the monitor keyword and describe the
methods as common methods :
monitor SimpleMonitor {
public method void testA(){
//Some code
}
public method int testB(){
return 1;
}
}
To describe a condition variable, we use the cond keyword. A condition variable is a kind of
queue of process who are waiting on the same condition. You have several operations available
on a condition, the most important is to signal a process waiting to be awaken and to wait on a
condition. There are some similarities between signal/wait operations and P and V of
semaphores, but this is a little different. The signal operation does nothing if the queue is empty
and the wait operation put always the thread in the waiting queue. The process queue is served in
a first come, first served mode. When a thread wakes up after waiting on a condition, it must
reacquire the lock before continuing in the code.
Before going further, we must have more information about the signal operations. When writing
monitors, you normally have the choice between several philosophies for the signaling operation
:
1. Signal & Continue (SC) : The process who signal keep the mutual exclusion and the
signaled will be awaken but need to acquire the mutual exclusion before going.
2. Signal & Wait (SW) : The signaler is blocked and must wait for mutual exclusion to
continue and the signaled thread is directly awaken and can start continue its operations.
3. Signal & Urgent Wait (SU) : Like SW but the signaler thread has the guarantee than it
would go just after the signaled thread
4. Signal & Exit (SX) : The signaler exits from the method directly after the signal and the
signaled thread can start directly. This philosophy is not often used.
The available policies depends on the programming language, in Java, there is only one policy
available, the SC one.
In Java there is no keyword to directly create a monitor. To implement a monitor, you must
create a new class and use Lock and Condition classes. Lock is the interface is ReentrantLock
is the main used implementation, this is the one that we'll learn to use in the current post. To
create a ReentrantLock, you have two constructors, a default constructor and a constructor with a
boolean argument indicating if the lock is fair or not. A fair lock indicates that the threads will
acquire the locks in the order they ask for. Fairness is a little heavier than default locking
strategies, so use it only if you need it. To acquire the lock, you just have to use the method lock
and unlock to release it.
The explicit locks have the same memory semantics than the synchronized blocks. So the
visibility of the changes is guarantee when you use lock()/unlock() blocks.
So to implement, the monitor example we've seen before, we just need to create a class and use
the lock to make the mutual exclusion :
try {
//Some code
} finally {
lock.unlock();
}
}
try {
return 1;
} finally {
lock.unlock();
}
}
}
The person who've already read the other parts of this post set will say that it will be easier to use
the synchronized keyword on the two methods. But with synchronized, we will not have the
condition variables. If you don't need condition variables but only locking, it will be easier to use
the synchronized blocks instead of Locks.
You can create conditions using the newCondition method on the lock. A condition is a variable
of type Condition. You can make the current thread wait on the condition using the await
method (and its variant with timeout) and you can signal threads using signal and signalAll
methods. The signalAll methods wakes up all the threads waiting on the condition variable.
Let's try with a simple common example : A bounded buffer. It's a cyclic buffer with a certain
capacity with a start and an end.
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
try {
while (count == capacity) {
notFull.await();
}
buffer[rear] = data;
rear = (rear + 1) % capacity;
count++;
notEmpty.signal();
} finally {
lock.unlock();
}
}
try {
while (count == 0) {
notEmpty.await();
}
notFull.signal();
return result;
} finally {
lock.unlock();
}
}
}
So some explications :
1. The two methods are protected with the lock to ensure mutual exclusion
2. Then we use two conditions variables. One to wait for the buffer to be not empty and an
other one to wait for the buffer to be not full.
3. You can see that I have wrapped the await operation on a while loop. This is to avoid
signal stealers problem that can occurs when using Signal & Continue
And that BoundedBuffer can be easily used with several threads with no problems.
As you can see, you can use monitors to solve a lot of concurrent programming problems and
this mechanism is really powerful and performing.
I hope you found that article interesting and that this set of posts about Java concurrency brings
you some stuff about Java.
This class works really well in single-threaded environment, but don't work at all when several
threads access the same Counter instance. If you don't know why, read this post about
synchronization. You can solve the problem using synchronized at method level :
This class now works well. But locking is not a lightweight mechanism and have several
disadvantages. When several threads try to acquire the same lock, one or more threads will be
suspended and they will be resumed later. When the critical section is little, the overhead is
really heavy especially when the lock is often acquired and there is a lot of contention. Another
disadvantage is that the other threads waiting of the lock cannot do something else during
waiting and if the thread who has the lock is delayed (due to a page fault or the end of the time
quanta by example), the others threads cannot take their turn.
The actual processors provide several instructions that simplify greatly the implementation of
these non-blocking algorithms, the most-used operation today is the compare-and-swap
operation (CAS). This operation takes three parameters, the memory address, the expected
current value and the new value. It atomically update the value at the given memory address if
the current value is the expected, otherwise it do nothing. In both cases, the operation return the
value at the address after the operation execution. So when several threads try to execute the
CAS operation, one thread wins and the others do nothing. So the caller can choose to retry or to
do something else. We often use this operation to implement another operation, the compare-
and-set. This method makes exactly the same things as CAS but return a boolean indicating if the
operation succeeded or not.
Before Java 5.0, this operation was not available directly to developer, but in Java 5.0 several
atomic variables (for int, long, boolean and reference values) were added. The int and long
versions also supports numeric operations. The JVM compiles these classes with the better
operations provided by the hardware machine, CAS or a Java implementation of the operation
using a lock. Here are the classes :
AtomicInteger
AtomicLong
AtomicBoolean
AtomicReference
All these classes supports compare-and-set (via the compareAndSet() method) and other
operations (get(), set() and getAndSet()). The setters operations are implemented using
compareAndSet. These classes supports multi-threaded access and have a better scalability than
synchronizing all the operations.
The incrementAndGet() and decrementAndGet() methods are two of the numeric operations
provided by the AtomicLong and AtomicInteger classes. You also have getAndDecrement(),
getAndIncrement(), getAndAdd(int i) and addAndGet().
This version is faster than the synchronized one and is also thread safe.
If you only have the compareAndSet(), here is how we can implement increment() method using
it :
if(integer.compareAndSet(current, next)){
return;
}
}
}
This seems to be complicated, but this is the cost of non-blocking algorithms. When we detect
collision, we retry until the operation succeeded. This is the common schema for non-blocking
algorithms.
while(true){
Element oldHead = head.get();
newElement.next = oldHead;
It's really more complicated than using synchronized on the two methods but also more
performing if there is contention (and often even if there is no contention).
So this ends this post. To conclude, atomic variables classes are a really good way to implement
non-blocking algorithms and moreover are also a very good alternative to volatile variables,
because they can provide atomicity and visibility.
This time we'll learn how to start cleanly new threads and to manage thread pools. In Java, if you
have a Runnable like this :
new Thread(runnable).start();
This is very simple and clean, but what if you've several long running tasks that you want to load
in parralel and then wait for the completion of all the tasks, it's a little bit harder to code and if
you want to get the return value of all the tasks it becomes really difficult to keep a good code.
But like for almost any problems, Java has a solution for you, the Executors. This simple class
allows you to create thread pools and thread factories.
Single Thread Executor : A thread pool with only one thread. So all the submitted task
will be executed sequentially. Method : Executors.newSingleThreadExecutor()
Cached Thread Pool : A thread pool that create as many threads it needs to execute the
task in parralel. The old available threads will be reused for the new tasks. If a thread is
not used during 60 seconds, it will be terminated and removed from the pool. Method :
Executors.newCachedThreadPool()
Fixed Thread Pool : A thread pool with a fixed number of threads. If a thread is not
available for the task, the task is put in queue waiting for an other task to ends. Method :
Executors.newFixedThreadPool()
Scheduled Thread Pool : A thread pool made to schedule future task. Method :
Executors.newScheduledThreadPool()
Single Thread Scheduled Pool : A thread pool with only one thread to schedule future
task. Method : Executors.newSingleThreadScheduledExecutor()
Once you have a thread pool, you can submit task to it using the different submit methods. You
can submit a Runnable or a Callableto the thread pool. The method return a Future representing
the future state of the task. If you submitted a Runnable, the Future object return null once the
task finished.
return "Run";
}
}
If you want to execute that task 10 times using 4 threads, you can use that code :
But you must shutdown the thread pool in order to terminate all the threads of the pool :
pool.shutdown();
If you don't do that, the JVM risk to not shutdown because there is still threads not terminated.
You can also force the shutdown of the pool using shutdownNow, with that the currently running
tasks will be interrupted and the tasks not started will not be started at all.
But with that example, you cannot get the result of the task. So let's get the Future objects of the
tasks :
pool.shutdown();
But this code is a bit complicated. And there is a disadvantage. If the first task takes a long time
to compute and all the other tasks ends before the first, the current thread cannot compute the
result before the first task ends. Once again, Java has the solution for you, CompletionService.
A CompletionService is a service that make easier to wait for result of submitted task to an
executor. The implementation is ExecutorCompletionService who's based on an ExecutorService
to work. So let's try :
With that, you have the result in the order they are completed and you don't have to keep a
collection of Future.
Here we are, you have the tools in hand to launch tasks in parralel using performing thread pools.
Using Executors, ExecutorService and CompletionService you can create complex algorithm
using several taks. With that tools, it's really easy to change the number of threads performing in
parralel or adding more tasks without changing a lot of code.
Fork/Join Framework
TrasnferQueue
ThreadLocalRandom
Fork/Join Framework
The most important improvement is a new Fork/Join Framework. Fork/Join is basically the
parralel version of the divide-and-conquer algorithm resolution. Here is the typical form of that
problems (taken from Doug Lea) :
Java 7 provide a new class ForkJoinPool to run ForkJoinTask. A ForkJoinTask is lighter than
a thread. If you have a lot of ForkJoinTask, you can host them with a smallest number of threads.
Two implementations of ForkJoinTask are provided :
From a ForkJoinTask you can invoke other task (fork them) using invokeAll methods.
So, now that we have covered the main concepts of this framework, we could start with a little
example (directly taken from Javadoc build 87). We'll use divide and conquer to increment all
the elements of an array. To know if the problem is small enough to solve it directly, we'll use a
threshold representing the number of elements that we can increment directly. If we have more
elements than the threshold, we will fork in two task otherwise, we'll compute directly the
incrementation on the array. So here is our task :
this.array = array;
this.low = low;
this.high= high;
}
@Override
protected void compute() {
if (high - low < THRESHOLD) {
for (int i = low; i < high; ++i){
array[i]++;
}
} else {
int mid = (low + high) >>> 1;
All the elements of the array will be incremented. Depending on the size of the array and of the
threshold, the problem will be divided in several sub problems and all these task will be managed
by the ForkJoinPool.
You can also make action that return something. By example, we can compute the sum of all the
elements of an array :
this.array = array;
this.low = low;
this.high= high;
}
@Override
protected Long compute() {
if (high - low < THRESHOLD) {
long sum = 0;
return sum;
} else {
int mid = (low + high) >>> 1;
right.fork();
You can also imagine others ways to divide the problems. An example is to compute the
THRESOLD left elements in the task and create a new task to compute the right elements. With
that, we create less tasks, but it depends on the context and on the problems. In practive, you'll
have normally more complex problems but if you can find a way to divide the problems, you
can use that new framework and have a very clean code.
TransferQueue
A new interesting collection. This collection is a blocking queue especially made for
producers/consumers. With that kind of queue, the producers can await for receipt of by the
consumers with a new transfer(E) method or like normal queue without waiting for receipt with
the put(E) method. It's also possible to make a transfer with timeout with the tryTransfer method.
There is no change in the consumer part, you always use take() to get an element and waiting for
an element. You've also access to the number of waiting consumer with the
getWaitingConsumerCount().
The implementation to use is the LinkedTransferQueue based on linked nodes. The elements
are ordered with FIFO. Here are some methods you can use with that new collection :
while(transfer.hasWaitingConsumer()){
//There is at least one consumer waiting for a transfer
}
It's also an interesting stuff. Useful by example in the case of message passing.
ThreadLocalRandom
A really simple but useful enhancement is the add of the ThreadLocalRandom class. This class
is a random number generator linked to the current Thread. It seems that if you use this
generator from two different thread, you will have two different random generators. The
generator is initialized with a generated seed that you cannot modify (setSeed() throws an
UnsupportedOperationException).
long l = ThreadLocalRandom.current().nextLong(22L);
If you always use this form, you have the guarantee that the random generator will never be
shared between two threads. Moreover, this new class provide methods to generate a bounded
numbers. By example, to generate a pseudo-random number between 10, inclusive and 33,
exclusive, you can type :
So here we are. I've covered the main features added on Java 7 for concurrency. I hope you find
that stuff interesting and that discovering this features will help you to make concurrent
programming in Java 7.