Concurrency and Threads in Java

Processes

Processes are often seen as synonymous with programs or applications. However, what the user sees as a single application may in fact be a set of cooperating processes. To facilitate communication between processes, most operating systems support Inter Process Communication (IPC) resources, such as pipes and sockets. IPC is used not just for communication between processes on the same system, but processes on different systems.
Most implementations of the Java virtual machine run as a single process. A Java application can create additional processes using a ProcessBuilder object.

Threads

Threads are sometimes called lightweight processes. Both processes and threads provide an execution environment, but creating a new thread requires fewer resources than creating a new process.
Threads exist within a process — every process has at least one. Threads share the process's resources, including memory and open files.

Thread Creation

  • To directly control thread creation and management, simply instantiate Thread each time the application needs to initiate an asynchronous task.
  • To abstract thread management from the rest of your application, pass the application's tasks to an executor.
//Implement Runnable
public class HelloRunnable implements Runnable {
    public void run() {
        System.out.println("Hello from a thread!");
    }
 
    public static void main(String args[]) {
        (new Thread(new HelloRunnable())).start();
    }
}
 
//Subclass Thread. The Thread class itself implements Runnable.
public class HelloThread extends Thread {
    public void run() {
        System.out.println("Hello from a thread!");
    }
    public static void main(String args[]) {
        (new HelloThread()).start();
    }
}
 
Another way:
 
new Thread(new Runnable() {
                public void run() { alphonse.bow(gaston); }
            }).start();

Important Methods

Sleep

Thread.sleep causes the current thread to suspend execution for a specified period. This is an efficient means of making processor time available to the other threads of an application or other applications that might be running on a computer system.
The thread does not lose ownership of any monitors (keeps the locks it holds)

sleep throws InterruptedException when another thread interrupts the current thread while sleep is active.

Interrupt

An interrupt is an indication to a thread that it should stop what it is doing and do something else. It's up to the programmer to decide exactly how a thread responds to an interrupt, but it is very common for the thread to terminate.
For the interrupt mechanism to work correctly, the interrupted thread must support its own interruption.

A thread sends an interrupt by invoking interrupt() on the Thread object for the thread to be interrupted.

Examples of supporting interruption:

for (int i = 0; i < importantInfo.length; i++) {
    try {
        Thread.sleep(4000);
    } catch (InterruptedException e) {
        // We've been interrupted...
       //do something...
    }
}
 
for (int i = 0; i < inputs.length; i++) {
    ...
    if (Thread.interrupted()) {
        // We've been interrupted...
        // do something
    }
}

Join

The join method allows one thread to wait for the completion of another (waits for this thread to die).
If we have a thread t, t.join() makes the current thread to wait for "t" to complete its execution and die.

        Thread t = new Thread(new Runnable() {
            public void run() { try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } }
        });
 
        t.start();
 
        t.join();//we wait here for run to complete and the resume from here.
        System.out.println("ding");//this is printed after 4 seconds

wait

This is a method on the Object class
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
The current thread must own this object's monitor (lock) to be able to call wait.
The thread releases ownership of this monitor.

notify

This is a method on the Object class
Wakes up a single arbitrary thread that is waiting on this object's monitor.
The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object.

notifyAll

This is a method on the Object class
Wakes up all threads that are waiting on this object's monitor.

Synchronization

Threads accessing shared data is efficient but makes two kinds of errors possible: thread interference and memory consistency errors. The tool needed to prevent these errors is synchronization.

Thread Interference

Interference happens when two operations, running in different threads, but acting on the same data, interleave. This means that the two operations consist of multiple steps, and the sequences of steps overlap.

Even simple statements can translate to multiple steps by JVM. For example the single expression c++ can be decomposed into three steps:

1- Retrieve the current value of c.
2- Increment the retrieved value by 1.
3- Store the incremented value back in c.

Memory Consistency Errors

Memory consistency errors occur when different threads have inconsistent views of what should be the same data.
The key to avoiding memory consistency errors is understanding the happens-before relationship.
This relationship is simply a guarantee that memory writes (assignments, changes) by one specific statement are visible to another specific statement.
There are several actions that create happens-before relationships. One of them is synchronization.

Example:

Suppose a simple int field is defined and initialized: int counter = 0;
The counter field is shared between two threads, A and B. Suppose thread A increments counter(counter++), Then, shortly afterwards, thread B prints out counter.
If the two statements had been executed in the same thread, it would be safe to assume that the value printed out would be "1". But if the two statements are executed in separate threads, the value printed out might well be "0", because there's no guarantee that thread A's change to counter will be visible to thread B — unless the programmer has established a happens-before relationship between these two statements.

We've already seen two actions that create happens-before relationships.

1- When a statement invokes Thread.start, every statement that has a happens-before relationship with that statement also has a happens-before relationship with every statement executed by the new thread. The effects of the code that led up to the creation of the new thread are visible to the new thread.
2- When a thread terminates and causes a Thread.join in another thread to return, then all the statements executed by the terminated thread have a happens-before relationship with all the statements following the successful join. The effects of the code in the thread are now visible to the thread that performed the join.

//TODO write more examples here

synchronized

In Java we have synchronized methods and synchronized statements.

Effects of a synchronized statement/method:
First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.
Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads.

Note that constructors cannot be synchronized — using the synchronized keyword with a constructor is a syntax error. Synchronizing constructors doesn't make sense, because only the thread that creates an object should have access to it while it is being constructed.

Intrinsic Locks

Synchronization is built around an internal entity known as the intrinsic lock or monitor lock.

Every object has an intrinsic lock associated with it. By convention, a thread that needs exclusive and consistent access to an object's fields (i.e. do synchronized method) has to own/acquire the object's intrinsic lock before accessing them, and then release the intrinsic lock when it's done with them. As long as a thread owns an intrinsic lock, no other thread can acquire the same lock. The other thread will block when it attempts to acquire the lock.

When a thread releases an intrinsic lock, a happens-before relationship is established between that action and any subsequent acquisition of the same lock.

When a thread invokes a synchronized method, it automatically acquires the intrinsic lock for that method's object and releases it when the method returns. The lock release occurs even if the return was caused by an uncaught exception.

When a static synchronized method is invoked, since a static method is associated with a class, the thread acquires the intrinsic lock for the Class object associated with the class. Thus access to class's static fields is controlled by a lock that's distinct from the lock for any instance of the class.

A thread can acquire the same lock more than once which is is called reentrant synchronization.

See Thread Safety in this page.

Atomic Access

An atomic action cannot stop in the middle: it either happens completely, or it doesn't happen at all. No side effects of an atomic action are visible until the action is complete.

Reads and writes are atomic for reference variables and for most primitive variables (all types except long and double).
Reads and writes are atomic for all variables declared volatile (including long and double variables).

We already saw that even simple statement such as a++ are not atomic.

Atomic actions cannot be interleaved, so there will be no thread interference. However, this does not eliminate all need to synchronize atomic actions, because memory consistency errors are still possible. Using volatile variables reduces the risk of memory consistency errors, because any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable.

What is volatile:
Changes to a volatile variable are always visible to other threads. When a thread reads a volatile variable, it sees not just the latest change to the volatile, but also the side effects of the code that led up the change.

Liveness

A concurrent application's ability to execute in a timely manner is known as its liveness.
liveness problems: deadlock, starvation and livelock

Deadlock

Deadlock describes a situation where two or more threads are blocked forever, waiting for each other.

Starvation

Starvation describes a situation where a thread is unable to gain regular access to shared resources and is unable to make progress. This happens when shared resources are made unavailable for long periods by greedy/long threads.

Livelock

A thread often acts in response to the action of another thread. If the other thread's action is also a response to the action of another thread, then livelock may result. As with deadlock, livelocked threads are unable to make further progress. However, the threads are not blocked, they are simply too busy responding to each other to resume work.

Guarded Blocks

Threads often have to coordinate their actions. The most common coordination idiom is the guarded block. Such a block begins by polling a condition that must be true before the block can proceed. There are a number of steps to follow in order to do this correctly.

public synchronized guardedJoy() {
    // This guard only loops once for each special event, which may not
    // be the event we're waiting for.
    while(!joy) {
        try {
            wait();
        } catch (InterruptedException e) {}
    }
}
 
public synchronized notifyJoy() {
    joy = true;
    notifyAll();
}
  • We use Object.wait() and Object.notifyAll() methods.
  • wait() suspends the current thread and releases the lock.
  • notifyAll() will notify waited threads to resume action.

There is a good example in Java tutorial.

Immutable Objects

An object is considered immutable if its state cannot change after it is constructed. Immutable objects are particularly useful in concurrent applications. Since they cannot change state, they cannot be corrupted by thread interference or observed in an inconsistent state.

This is one way of creating an immutable object:

1- Don't provide "setter" methods.
2- Make all fields final and private.
3- Don't allow subclasses to override methods (make class final).
4- If the instance fields include references to mutable objects, don't allow those objects to be changed.
5- Instead of changing the current object return a new object.

Programmers are often reluctant to create immutable objects because they worry about the cost of creating a new object as opposed to updating an object in place. The impact of object creation is often overestimated, and can be offset by some of the efficiencies associated with immutable objects. These include decreased overhead due to garbage collection, and the elimination of code needed to protect mutable objects from corruption.

See an example in Java tutorial having two version of the same class one synchronized one not: http://docs.oracle.com/javase/tutorial/essential/concurrency/imstrat.html

Executors

There are new ways of creating and working with threads in java.util.concurrent than what we have seen so far.

Executor

A simple interface that supports launching new tasks.

If r is a Runnable object, and e is an Executor object you can replace
 
(new Thread(r)).start();
 
with
 
e.execute(r);

ExecutorService

A subinterface of Executor, which adds features that help manage the lifecycle, both of the individual tasks and of the executor itself.

public class MySynExecutor implements Executor{
    public void execute(Runnable r) {
        r.run();
    }
}
 
public class Thread1 implements Runnable{
    public void run() { }
}
 
Thread1 r = new  Thread1();        
MySynExecutor mec = new MySynExecutor();
mec.execute(r);

ScheduledExecutorService

  • A subinterface of ExecutorService, supports future and/or periodic execution of tasks.
  • A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation. The result can only be retrieved using method get when the computation has completed, blocking if necessary until it is ready.
  • The Callable interface is similar to Runnable, in that both are designed for classes whose instances are potentially executed by another thread. A Runnable, however, does not return a result and cannot throw a checked exception.
  • In JavaEE we have ManagedExecutorService
ScheduledExecutorService es = Executors.newScheduledThreadPool(4);
Task<String> t = new Task();
Future<String> output = es.submit(t);
 
public class Task<V> implements Callable<V>{
    @Override
    public V call() throws Exception {
        return ...;
    }
}

Fork/Join

@since 1.7
It is designed for work that can be broken into smaller pieces recursively.

Basic use is like :
if (my portion of the work is small enough)
do the work directly
else
split my work into two pieces
invoke the two pieces and wait for the results

ForkJoinPool pool = new ForkJoinPool();
MyTask wp = new MyTask();
pool.execute(wp);
 
public class MyTask extends RecursiveAction {
    @Override
    protected void compute() {
        if(smalltask())
            //do it right away
        else
            invokeAll(new MyTask(), new MyTask());
    }
}

Concurrent Collections

The java.util.concurrent package includes a number of additions to the Java Collections Framework. All of these collections help avoid Memory Consistency Errors by defining a happens-before relationship between an operation that adds an object to the collection with subsequent operations that access or remove that object.

Examples are: BlockingQueue, concurrentMap, etc

Atomic Variables

The java.util.concurrent.atomic package defines classes that support atomic operations on single variables. All classes have get and set methods that work like reads and writes on volatile variables. That is, a set has a happens-before relationship with any subsequent get on the same variable.
These can be used in counter classes and instead of using synchronized method we can use AtomicInteger. Notice that AtomicInteger is not a replacement for Integer and should not be used in normal situations.

ThreadLocal

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection.

Thread Safety

Definitions

In fact, problems only arise if one or more threads want to write to resources. It is safe to let multiple threads read the same resources, as long as the resources do not change.
The situation where two threads compete for the same resource, where the sequence in which the resource is accessed is significant, is called race conditions. A code section that leads to race conditions is called a critical section.
Code that is safe to call by multiple threads simultanously is called thread safe. If a piece of code is thread safe, then it contains no race conditions. Race condition only occur when multiple threads update shared resources.

//not thread safe
public class Counter {
     protected long count = 0;
     public void add(long value){
         this.count = this.count + value;   
     }
  }

Local primitive variables

When you create a thread it will have its own stack created. Two threads will have two stacks and one thread never shares its stack with other thread.
Stack keeps local (method) variables and method parameters.
All local variables defined in your program will be allocated memory in stack and are thread safe.

//thread safe local primitive variable
public void someMethod(){
    long threadSafeInt = 0;
    threadSafeInt++;
}

Object references

Local references to objects are a bit different. The reference itself is not shared. The object referenced however, is not stored in each threads's local stack. All objects are stored in the shared heap. If an object created locally never escapes the method it was created in, it is thread safe. In fact you can also pass it on to other methods and objects as long as none of these methods or objects make the passed object available to other threads.

//thread safe
public void someMethod(){
  LocalObject localObject = new LocalObject();
  localObject.callMethod();
  method2(localObject);
}
public void method2(LocalObject localObject){
  localObject.setValue("value");
}

In this example, the LocalObject instance is not returned from the method, nor is it passed to any other objects that are accessible from outside the someMethod() method. Each thread executing the someMethod() method will create its own LocalObject instance and assign it to the localObject reference. Therefore the use of the LocalObject here is thread safe. In fact, the whole method someMethod() is thread safe. Even if the LocalObject instance is passed as parameter to other methods in the same class, or in other classes, the use of it is thread safe. The only exception is of course, if one of the methods called with the LocalObject as parameter, stores the LocalObject instance in a way that allows access to it from other threads.

Object Members

Object members are stored on the heap along with the object and are not thread safe. Therefore, if two threads call a method on the "same" object instance and this method updates object members, the method is not thread safe.

//If two threads call the add() method simultanously on the "same" instance then it leads to race conditions. 
//If this happens on two different instances of the object then we will have no race condition.
public class NotThreadSafe{
    StringBuilder builder = new StringBuilder();
    public add(String text){
        this.builder.append(text);
    }    
}

Thread Safety and Immutability

We can make sure that objects shared between threads are never updated by any of the threads by making the shared objects immutable, and thereby thread safe. Here is an example:

//Immutable class
public class ImmutableValue{
  private int value = 0;
  public ImmutableValue(int value){
    this.value = value;
  }
  public int getValue(){
    return this.value;
  }
}

Reference is not Thread Safe

Even if an object is immutable and thereby thread safe, the reference to this object may not be thread safe.

//ImmutableValue class is thread safe, but the use of it is not.
public void Calculator{
  private ImmutableValue currentValue = null;
  public ImmutableValue getValue(){
    return currentValue;
  }
  public void setValue(ImmutableValue newValue){
    this.currentValue = newValue;
  }
  public void add(int newValue){
    this.currentValue = this.currentValue.add(newValue);
  }
}

To make the Calculator class thread safe you could have declared the getValue(), setValue(), and add() methods synchronized. That would have done the trick.

Thread-safety/concurrency in web

In a multithreaded server, shared resources can be accessed concurrently. In addition to scope object attributes, shared resources include in-memory data, such as instance or class variables, and external objects, such as files, database connections, and network connections. Concurrent access can arise in several situations:

  • Multiple web components accessing objects stored in the web context.
  • Multiple web components accessing objects stored in a session.
  • Multiple threads within a web component accessing instance variables.

A web container will typically create a thread to handle each request.

To ensure that a servlet instance handles only one request at a time, a servlet can implement the SingleThreadModel interface. The SingleThreadModel Interface is deprecated in servlet spec 3.0.

From servlet spec 3.0:

The handling of concurrent requests to a Web application generally requires that the Web Developer design servlets that can deal with multiple threads executing within the service method at a particular time. Generally the Web container handles concurrent requests to the same servlet by concurrent execution of the service method on different threads.

A servlet container may send concurrent requests through the service method of the servlet. To handle the requests, the Servlet Developer must make adequate provisions for concurrent processing with multiple threads in the service method. One way is to use SingleThreadModel which is not recommended. Other way is to synchronized keyword on servlet methods which is not recommended due to performance issues.

Multiple servlets executing request threads may have active access to the same session object at the same time. The container must ensure that manipulation of internal data structures representing the session attributes is performed in a thread safe manner. The Developer has the responsibility for thread safe access to the attribute objects themselves. This will protect the attribute collection inside the HttpSession object from concurrent access, eliminating the opportunity for an application to cause that collection to become corrupted.

EJB Thread Safety

from ejb 3.2 spec:
"The container serializes calls to each stateful and stateless session bean instance. Most containers will support many instances of a session bean executing concurrently; however, each instance sees only a serialized sequence of method calls. Therefore, a stateful or stateless session bean does not have to be coded as reentrant."

This thing from the spec means that stateful and stateless session beans are thread safe.

EJB and Transactions

By default if no transaction demarcation is specified, enterprise beans use container-managed transaction demarcation.

Typically, the container begins a transaction immediately before an enterprise bean method starts and it commits the transaction just before the method exits.
Each method can be associated with a single transaction. Nested or multiple transactions are not allowed "within a method" but a method can choose to run in a new transaction using RequiresNew tx attribute. This is useful if you need to make changes of this method visible to other threads/EJBs/Users.

Container-managed transactions do not require all methods to be associated with transactions. When developing a bean, you can specify which of the bean’s methods are associated with transactions by setting the transaction attributes.

Imagine we have an EJB method that does a database save:

EJB class:
some_method(){
save(object)
JPA flush() //this is not commit yet

}

If two clients call this method at the same time the result of each save is not visible to other client/thread unless some_method finishes/commits. JPA flush will make this object visible to the subsequent queries in the same tx only.

Uncommitted data are "visible" from subsequent queries "within" a transaction. They are not visible to other threads/transactions/users until method finish/commit.
If you want the result of this save be visible to other transaction you have to create a new tx for this method using RequiresNew.

Reference

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License