Concurrency in Java – A Beginner’s 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.

What are Threads

Conceptually, a thread is a distinct flow of control within a program. A thread is similar to the more familiar notion of a process, except that multiple threads within the same application share much of the same state–in particular, they run in the same address space or memory space.

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 don’t hold their own state (except when you use ThreadLocal) but instead rely on the program’s 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.

What is Thread Safety

Thread safety is an invariable attempt to design class constructs so that their objects or instances, when accessible from multiple threads, still demonstrate the idempotent behavior as they would if accessed from a single threaded model.

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 System’s 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.

How to make classes Thread Safe.

At a high level, thread safety is a very simple concept, theoretically. If we allow free reading access to an object’s state but control the writing access we can make the object 100% thread safe. This is the most common sense way to do it. We need to take care of two things here:

      • 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 don’t 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 don’t 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 it’s 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 don’t 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
    public class EmployeeManager2 {
        /*
         * Mutating operations are encapsulated inside the class.
         * State (employees) is not shared outside class directly.
         */
        private List<Employee> employees;
        
        public void addEmployee(Employee e){
           //do some validations or preprocessing
            synchronized(this){
                //only this code blocks
                this.employees.add(e);
            }
            //do somehting else
        }
    
        /*
         * This is the right way to get data from collections.
         * Finding by index is not safe as index may change over time.
         */
        public Employee getEmployee(String name)
                        throws CloneNotSupportedException{
            //get the employee from the list
            Employee employee = this.findEmployeeByName(name);
            if(employee != null){
                return (Employee)(employee.clone());
            }else{
                return null;
            }
        }
    
        public void deleteEmployee(String name){
            //get the employee from the list
            synchronized(this){
                for (Iterator<Employee> it = employees.iterator(); it.hasNext();) {
                    Employee employee = it.next();
                    if(employee.getName().equals(name)){
                        it.remove();//delet the employee
                        break;
                    }
                }
            }
        }
    
        private Employee findEmployeeByName(String name){
            for (Iterator<Employee> it = employees.iterator(); it.hasNext();) {
                Employee employee = it.next();
                if(employee.getName().equals(name)){
                    return employee;
                }
            }
            return null;
        }
    }
    

    Object Behavior Heuristics

    EmployeeManager2 gave a simple example about how to make use of thread safe constructs without compromising on concurrency. However as the programs evolve in size, the problem of thread safe also increases exponentially. e.g. assume that we have to calculate an employee’s total CTC package and tax structures also and should be available for reading once the employee is loaded in the list.

    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 don’t 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.

    Consider the following harmlessly looking code:

    public class Temp {
    
        public static void main(String[] args) {
            int i=0;
            i++;
            System.out.println(i);
        }
    }
    

And this is what the above code actually translates into. We can verify that using the javap command tool with the disassemble flag.

image

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?

Consider the following code example.


@NotThreadSafe
public class ConnectionPoolWatchDog1 {

    private AtomicInteger borrowers; //we increment this counter
    private AtomicInteger connections; //we decrement thsi counter

    public ConnectionPoolWatchDog1() {
        this.borrowers = new AtomicInteger(1);
        this.connections = new AtomicInteger(10);
    }

    public void borrowOne() {
        //do some tasks
        //increment the borrowers count
        this.borrowers.getAndIncrement();
        //decrement the remaining conenction count
        this.connections.getAndDecrement();
        //do some other tasks
    }

    public void returnOne(){
        //do some tasks
        //increment the remaining conenction count
        this.connections.getAndIncrement();
        //decrement the borrowers count
        this.borrowers.getAndDecrement();
        //do some other tasks
    }
}

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 it’s dependent socket is blocked or non-responsive. So it’s 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.

To solve this problem we have to rely on plain old locking idioms.

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.


@ThreadSafe
public class ConnectionPoolWatchDog2 {
    //we increment this counter
    @GuardedBy("this") private AtomicInteger borrowers;

    //we decrement this counter
    @GuardedBy("this") private AtomicInteger connections; 

    public ConnectionPoolWatchDog2() {
        this.borrowers = new AtomicInteger(1);
        this.connections = new AtomicInteger(10);
    }

    public void borrowOne() {
        //do some tasks
        synchronized(this){
            //increment the borrowers count
            this.borrowers.getAndIncrement();
            //decrement the remaining conenction count
            this.connections.getAndDecrement();
        }
        //do some other tasks
    }

    public void returnOne(){
        //do some tasks
        synchronized(this){
            //increment the remaining conenction count
            this.connections.getAndIncrement();
            //decrement the borrowers count
            this.borrowers.getAndDecrement();
        }
        //do some other tasks
    }
}

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 don't 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.

public class Temp2 {
    private int age = 10;
    private synchronized void hello(String name, int age){
        String temp = name + " there";
        synchronized(this){
            this.age = age;
        }
        System.out.println(temp + " age " + age);
    }

    public static void main(String[] args){
        Temp2 t2 = new Temp2();
        t2.hello("user", 20);
    }
}

The above code demonstrated the concept of reentrant synchronization. Below figure is the disassembled version of above code.

image

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.

State Visibility Guarantees

Locking idioms not just helps us in controlling access around program ‘critical paths’ but also allows us to coordinate access and guarantee consistency and durability of sate visibility operations. This is an important consideration which JVM specification offers as a subtle guarantee. Without this guarantee there would actually be little or no sense in actually employing synchronization to control thread access operations on shared data. The JVM specs guarantee that while a thread is blocked waiting for the lock, since another thread is using it, it does not see any changes to the state invariants until the ‘block’ is opened. Once the blocking thread owns the lock, it sees all and complete changes that the previous thread made to the shared invariants.

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.

  • Word Tearing Phenomenon
  • Volatile modifier

Word Tearing Phenomenon

Word tearing is a significant aspect of JVM implementations which every developer needs to be aware of. Essentially the java specification and memory model explicitly talk about this pitfall that is directly related to processor architectures and behaviors.

From a JVM implementation perspective (and not a Java Program’s 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 don’t 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:

  • reading the whole word first
  • updating the appropriate byte offset
  • writing the whole word back

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 don’t 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. That’s 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 following code example can fail.

public class Temp3 {

    private boolean isReady;

    public static void main(String[] args) {
        Temp3 t3 = new Temp3();
        while(!t3.isReady){
            try {
                System.out.println("Not ready dear.");
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
}

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 what’s 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). That’s 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.

Design Thread Safe Classes and Components

Designing thread safe classes is not a herculean task but rather a simple ‘play by the book’ set of rules to be followed and adhered to. This section outlines a set of basic guidelines to follow to design thread safe classes. As long as we stick to basic OOP principles of encapsulation and abstraction Thread safe class construction is not a big deal.

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

Design Principles – Shared State Ownership

One of the primary rules to ensure thread safety is to outline shared state ownership rules. These rules typically answer the following questions without any reservations:

  • Who owns the shared state
  • Who can mutate this shared state and how
  • Who can access this shared state without locking and how
  • Who should lock and wait while the state updates

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.

public class EmployeeManager3 {

    private Map<Integer, Employee> employees =
            Collections.synchronizedMap(new HashMap<Integer, Employee>());

    public void addNewEmployee(Employee employee) {
        this.employees.put(employee.getEmployeeId(), employee);
    }

    public boolean updateEmployee(Employee employee) {
        if (this.employees.containsKey(employee.getEmployeeId())) {
            this.employees.put(employee.getEmployeeId(), employee);
            return true;
        } else {
            return false;
        }
    }

    public boolean deleteEmployee(Employee employee) {
        if (this.employees.containsKey(employee.getEmployeeId())) {
            this.employees.remove(employee.getEmployeeId());
            return true;
        } else {
            return false;
        }
    }

    public Employee getEmployee(int employeeId) {
        if (this.employees.containsKey(employeeId)) {
            return this.employees.get(employeeId);
        } else {
            return null;
        }
    }
}

The addNewEmployee, updateEmployee, deleteEmployee and getEmployee methods are not synchronized within their own context but rely on the synchronization aspects provided by the Collections.synchronized API. Now assuming two threads are working together to update the same employee and read it in parallel. So while thread 1 reaches line 30 and receives the employee thread 2 is in process of updating the same guy. This could lead to stale data. Image the same scenario but this time one thread is reading the employee and another thread is trying to delete it. So while the reading thread has completed the ‘contains’ statement at line 29 and found the employee exists the deleting thread executes the line 21 and deletes it.

To fix these problems we need to synchronize the EmployeeManager to allow only 1 operation at a time. How bout this?

public class EmployeeManager4 {

    private Map<Integer, Employee> employees =
            Collections.synchronizedMap(new HashMap<Integer, Employee>());

    public synchronized void addNewEmployee(Employee employee) {
        this.employees.put(employee.getEmployeeId(), employee);
    }

    public synchronized boolean updateEmployee(Employee employee) {
        if (this.employees.containsKey(employee.getEmployeeId())) {
            this.employees.put(employee.getEmployeeId(), employee);
            return true;
        } else {
            return false;
        }
    }

    public synchronized boolean deleteEmployee(Employee employee) {
        if (this.employees.containsKey(employee.getEmployeeId())) {
            this.employees.remove(employee.getEmployeeId());
            return true;
        } else {
            return false;
        }
    }

    public synchronized Employee getEmployee(int employeeId) {
        if (this.employees.containsKey(employeeId)) {
            return this.employees.get(employeeId);
        } else {
            return null;
        }
    }
}

is this class thread safe now? The answer is No.

Why?

Because this class synchronizes on ‘this’ object where as it should synchronize on ‘employees’. This is what the Javadocs for Collections.synchronizedMap state.

image

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’ won’t help in blocking the access either. It’s the map operations which we need to make atomic not our own code. So we must synchronize on the employees’ variable instead of ‘this’.

The following code example shows it correctly:

public class EmployeeManager5 {

//    private final Map<Integer, Employee> employees =
//            Collections.synchronizedMap(new HashMap<Integer, Employee>());

    private Map<Integer, Employee> employees =
            Collections.synchronizedMap(new HashMap<Integer, Employee>());

    public void addNewEmployee(Employee employee) {
        synchronized(employees){
            this.employees.put(employee.getEmployeeId(), employee);
        }
    }

    public boolean updateEmployee(Employee employee) {
        synchronized(employees){
            if (this.employees.containsKey(employee.getEmployeeId())) {
                this.employees.put(employee.getEmployeeId(), employee);
                return true;
            } else {
                return false;
            }
        }
    }

    public boolean deleteEmployee(Employee employee) {
        synchronized(employees){
            if (this.employees.containsKey(employee.getEmployeeId())) {
                this.employees.remove(employee.getEmployeeId());
                return true;
            } else {
                return false;
            }
        }
    }

    public Employee getEmployee(int employeeId) {
        synchronized(employees){
            if (this.employees.containsKey(employeeId)) {
                return this.employees.get(employeeId);
            } else {
                return null;
            }
        }
    }

This is the right way to do it. This was an example where incorrect synchronization policies and half-baked state ownership rules play havoc.

Design Principles – Instance Confinement

Instance confinement is a technique which uses encapsulation and a couple of design pattern para-diagrams to restrict access to shared mutable data. This technique instills the discipline to make the encapsulating class or its subclass the owner of shared mutable data and does not allow direct data mutation from outside. In some cases a limited mutation may be allowed but is controlled using thread confinement techniques.

EmployeeManager5 is a good example of an instance confined thread safe class (though not 100%).

  • All operations on the ‘employees’ is encapsulated inside.
  • EmployeeManager5 is about 95% thread because its methods addNewEmployee, updateEmployee, deleteEmployee are thread safe.
  • The ‘getEmployee’ is not 100% thread safe as it lets a live Employee object escape from the EmployeeManager5 class. To fix this we should make a copy of the employee once it is received form the map and then return that copy.
  • EmployeeManager5 does not handle serialization effectively.

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 can’t 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.

Instance confinement is largely done using the design techniques of

  • Delegation
  • Composition
  • Private Monitor Pattern

Instance Confinement - Delegation

Delegation thread safety principle promotes use of existing thread safe constructs and encourages the main classes to delegate the thread safety work to existing thread safe classes instead of making their own. The idea is simply to “use what you can (and should) and create what you cant”. Since we use the thread safety guarantees offer by existing classes and constructs we don’t need thread safe or locking idioms in our code.

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 don’t 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.

Instance Confinement - Composition

Delegation thread safety principle owns its roots to another Gang Of Four (GOF) prevalent design principle named as Composition. In simple terms, GOF principles encourage us to make abundant use of composition as against inheritance to add functionality and behavior.

Let’s 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.

    As discussed earlier, synchronized collections in the Collection API make use of Approach 2 to make all existing collection types thread safe. Inheritance approach seems good and viable when you have a few behaviors to add and new behaviors are not actually an atomic view of existing compound behaviours. e.g. ‘getIfPresent’ is actually a compound of ‘if contains then get’. When we need to add many of such new behaviors it’s better to use composition instead of inheritance.

    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. It’s an invariant dependent on the owner of a car.

Thread safety also works on the same lines.

Private Monitor Pattern

Private monitor pattern, also called java monitor pattern, is a direct derivative to the encapsulation principle. Normally the strict encapsulation principles applied to data variables is to hold them as private instance variables instead of public or protected. This allows the class instance to encapsulate the data it owns. Secondly to allow selective mutation of data variables and free viewing of the current data variable state, the class can expose public accessors and mutator methods. The mutator methods can implement conditional logic to selectively mutate the state values.

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 don't 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.

@ThreadSafe
public class EmployeeManager6 {
    private List<Employee> emplyees;
    private final Object lockMonitor = new Object();

    public EmployeeManager6(){
        this.emplyees = new ArrayList<Employee>();
    }

    public Employee getEmployee(int index)
                throws CloneNotSupportedException {
        synchronized(this.lockMonitor){
            return (Employee)(this.emplyees.get(index)).clone();
        }
    }

    public void addEmployee(Employee employee) {
        synchronized(this.lockMonitor){
            this.emplyees.add(employee);
        }
    }

    public void deleteEmployee(int employeeId) {
        synchronized(this.lockMonitor){
            for (Iterator<Employee> it = emplyees.iterator(); it.hasNext();) {
                Employee emp = it.next();
                if(employeeId == emp.getEmployeeId()){
                    it.remove();
                    break;
                }
            }
        }
    }

    public void deleteEmployee(Employee employee) {
        synchronized(this.lockMonitor){
            this.emplyees.remove(employee);
        }
    }

    /*
     * returns an unmodifiable list.
     * Can also return a copy of the original list.
     *
     */
    public List<Employee> getAllEmployees(){
        return Collections.unmodifiableList(this.emplyees);
    }
}

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 don't need to take any additional synchronization steps as long as they don't 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 .

public class EmployeeManager6Client {
    private EmployeeManager6 employeeManager6;

    public EmployeeManager6Client(){
        this.employeeManager6 = new EmployeeManager6();
    }

    public void deleteEmployeeRecord(int employeeId){
        try {
            Employee employee = this.employeeManager6.getEmployee(employeeId);
            this.employeeManager6.deleteEmployee(employee);
        } catch (CloneNotSupportedException ex) {
            ex.printStackTrace();
        }
    }
}

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.

image

But the worst part is that EmployeeManager6Client can’t 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 don’t 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.

Thread Confinement - Implementation Owned

As discussed earlier, proper encapsulation and abstraction principles ensure that mutable data is never escaped form the implementation classes or components. When the implementation of frameworks and APIs fully own the responsibilities of keeping data thread safe under all circumstances, it becomes naturally easy for their clients to work through the code. The implementations should also ensure that data serialization semantics does not garble the data sanity. Serialization mechanisms if followed wrongly can create thread safety hazards in the surfaced objects. Since serialization would create a new copy of our original object so we need to make sure that the new cop is also thread safe. Inheritance is another scenario that implementations need to take care of. Subclasses can easily override the super class methods and play havoc with the state data if the state is not corrected encapsulated and hidden. The implementations should ensure that subclasses don’t get direct control to mutate the shared invariants.

Thread Confinement - Thread Stack Confined

This is a special type of thread confinement where the state can only be reached through the local variables since the local variables which the thread creates are incidentally confined to only that thread’s execution stack. One example of this is method parameters and their copies within a method. Another example is the variour local variables which we define inside blocks of code.

Consider the following example screenshot.

image

In the adjoining example the local variable tempEmployee is totally thread safe by design. Since all threads entering the method ‘updateEmployee’ would own their own copies of tempEmployee so two threads would never cross each other on this. We cannot violate its thread safety even if we want to.

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 don’t 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.

Thread Confinement – Working with safe copies

Safe copies, also called as Shared Thread Safe Copies, extend the Thread Stack confinement idea to a bit bigger level. Same principles apply but at the inter class or inter component data sharing level. The previous code example above made use of clone method to create a copy of the employee parameter before analyzing it in the employees List. This is essentially the use of prototype pattern. Another beautiful use of the same idea is shown in the code example below where we read the employee from the list and return it to the caller..

/*
     * This is the right way to get data from collections.
     * Finding by index is not safe as index may change over time.
     */
    public Employee getEmployee(String name)
                    throws CloneNotSupportedException{
        //get the employee from the list
        Employee employee = this.findEmployeeByName(name);
        if(employee != null){
            return (Employee)(employee.clone());
        }else{
            return null;
        }
    }

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 don’t find their way back in the list unless someone tried to update the employee again.

Thread Confinement – Thread Local copies

TheadLocal is a more API inclined way of dealing with Thread confinement design. Java SDK offers excellent API semantics to make variables and state thread inclined using the TheadLocal class. Variables that are declared as TheadLocal are essentially local to each thread scope which tries to access them. This means that each thread which tried to access them has its own copy of the variable on which it works and this copy is private to the thread’s scope. So threads don’t share the same TheadLocal variable but make copies of it, for their own use. This also consequently means that Threads cannot influence or mutate TheadLocal variable present inside other threads. This makes it a wonderful thread confinement mechanism. TheadLocal instances are typically private static fields in classes because it makes no sense to declare them as instance variables.

TheadLocal is used exhaustively in common middleware technologies like JMS, stateful connection pools, message dispatchers etc. One of the most important and frequent use of TheadLocal is for random number generation for use in security round robins and database primary key generation. The following code example inspired by the TheadLocal JDK API Javadocs shows a prominent use of TheadLocal.

public class SafeCounter {
    // Atomic integer containing the next thread ID to be assigned
    private static final AtomicInteger nextId = new AtomicInteger(0);
    
    // Thread local variable containing each thread's ID
    private static final ThreadLocal<Integer> threadId =
            new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return nextId.getAndIncrement();
        }
    };

    // Returns the current thread's unique ID, assigning it if necessary
    public static int get() {
        return threadId.get();
    }
}

//TODO: Explain the code above a bit.

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 .

public class HibernateUtil {

    private static final Log log = LogFactory.getLog(HibernateUtil.class);
    /**
     * Read the configuration, will share across threads*
     */
    private static SessionFactory sessionFactory;
    /**
     * the per thread session *
     */
    private static final ThreadLocal<Session> currentSession = new ThreadLocal<Session>();
    /**
     * The constants for describing the ownerships *
     */
    private static final Owner trueOwner = new Owner(true);
    private static final Owner fakeOwner = new Owner(false);
    /**
     * set this to false to test with JUnit *
     */
    private static final boolean isLife = true;

    /**
     * get the hibernate session and set it on the thread local. Returns
     * trueOwner if it actually opens a session
     */
    public static Object createSession() throws Exception {
        Session session = (Session) currentSession.get();
        //System.out.println(session);
        if (session == null) {
            //System.out.println("No Session Found - Create and give the identity");
            session = getSessionFactory().openSession();
            currentSession.set(session);
            return trueOwner;
        }
        //System.out.println("Session Found - Give a Fake identity");
        return fakeOwner;
    }

Use of TheadLocal has two primary limitations.

State Sharing Only

  • 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 doesn’t 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

Managed Thread Pools

  • 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.

SEVERE: A web application registered the JBDC driver [com.jdbc.mysql.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.

Thread Confinement – Immutability and Final

Immutable objects and final references are the two most debated and disputed aspects of Java programming model. Immutability essentially states that once the object is created and initialized its internal state cannot be change again. Because its internal state cannot be changed again it automatically becomes thread safe once it is created. Such an immutable object or defensive copies, as they are also called, can be passed to untrusted client code which may try to mutate it. Then again any mutating operation on the immutable object creates a new object to the same type with the new target state instead of altering the existing one. Thus immutable objects are always thread-safe.

This post doesn’t 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.

Consider the following code example.

public class Temp6 {
    private String name;

    public String getName() {
        synchronized(this.name){
            return name;
        }
    }

    public void setName(String name) {
        synchronized(this.name){
            this.name = name;
        }
    }
}

Though name is a String and is immutable in above code and let’s 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.

How many of us know if:

  • 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 don’t know about point 1 and 2. This is because the framework owners or creators didn’t 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.

Conclusion and Closure

This article walked us through the fine points of thread safety in Java. We examined and understood why should we care about thread safety, what does it entail, and how to ensure our components are thread safe. The article also walked us through the various design principles that should be used to always be on the safe side of the multithreaded para-diagram.

About these ads

5 Responses to Concurrency in Java – A Beginner’s introduction

  1. Ashok says:

    It is really very help full

  2. Gaurav Verma says:

    Very informative. Almost all the concepts are covered here related to multi-threading and concurrent processes in a very fascinating way.

  3. Naveed says:

    can any body give me the source code of above examples because I want to run them

  4. Satish says:

    awesome article

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 275 other followers

%d bloggers like this: