Java Concurrency Overview Part 1

Modern computers can have several processors or several cores in one processor and this offers big opportunities in developing applications. Because traditionally, applications have been developed for consecutive computation and as result to solve many complex problems you had to spend more time, money and other resources. Nowadays such programming languages as Java provides all necessary tools to make a multithreaded application.

cpu_die

Let’s get started from basics and go ahead for more complex things like Java concurrent package, but we will do it serially.

Processes and threads

In Java mainly you are dealing with threads rather than processes, but processes are also important. The difference between these two units of execution is that a process runs independently, it is isolated of other processes and cannot directly access resources of other process. For instance, an application can be considered as a process.

A thread is running within a process and shares memory with all threads of the same process. For instance, when you execute java application you start a process and inside the process at least one thread (main thread) is created.

Memory model

In JVM each running thread has its own stack of methods. Each method contains information about its local variables. All primitive local variables such as int, long, float, double, boolean, byte, short and char are stored directly in the stack and not visible to other threads. Besides primitive variables the stack can contain references to the objects that stored in the heap. Because it doesn’t matter where you created an object it always will be stored in the heap and local local variables can obtain only references to the objects in the heap.

jmm

Thus, the key point here is that primitive variables are not shared among threads. But objects can be shared. For instance, you created an object inside a method and then returned it back to the caller method. The static variables are stored in the heap.

Another memory space you should know is hardware memory. As it was mentioned modern computers can have more than one processor or core. The architecture of the processors of course differ. As an example it can be designed like I illustrated on the diagram below.

hard_mem

Whenever a core execute instruction it can operate with information that can be stored either in registers, L1/L2 cache, or in RAM. The difference here is in speed of access information. Registers are the closest memory space where core can perform read/write operations. Therefore it is the fastest way to operate with information followed by L1/L2 cache and RAM.

Although we are storing variables in the heap or stack the CPU during execution of the thread can store variable in register, cache or RAM.

Race conditions

When two or more threads share and change the same resources at the same time a race condition occurs. Since you cannot predict when a particular thread accesses shared data and the end result will be nondeterministic. For example, we have a condition and if the condition is true we need to change shared object:

If this code will be executed by two threads at the same time the result might be inconsistent.

Creating and starting a new thread

There are two ways to specify code which will be executed inside a thread. The first way is to extend class java.lang.Thread and override run method.

public class NewThread  extends Thread {

    @Override
    public void run() {
        // write your code here
    }
    
}

Another option allows you to implement java.lang.Runnable interface and again override run method.

public class NewRunnable implements Runnable {
    
    @Override
    public void run() {
        // write your code here   
    }
    
}

There are no crucial difference between these two option. Both ways work and your choice depends on application design.

Here go exmples on how to start a thread.

// Thread
NewThread thread1 = new NewThread();
thread1.setName("My thread name");
thread1.start();

// Runnable
Thread thread2 = new Thread(new NewRunnable(), "Another thread");

Thread lifecycle

There are five states that a thread can have. The states controlled by scheduler and you do not  have full control on it.

thread_lifecycle

  • New – Initially when you create an instance of a thread and didn’t call start() method then the thread is in new state.
  • Runnable – generally a thread can be runnable in two cases. When you call the start() method or when the thread scheduler moves a thread from waiting/blocking state to runnuble. Runnable means that a thread is ready for execution.
  • Running – a thread in running state when it is executing by JVM.
  • Waiting/Blocking – in this state a thread is not running. It can wait another thread or sleep.
  • Dead – once the method run() is executed the scheduler moves thread to dead state. There are no way back, for example if you call start() method again you get an exception.

Synchronized blocks and methods

The synchronization is a tool that is used by developer to control access to the shared resources. In java, synchronization is done through acquiring the monitor on some object. Every object and class is associated with monitor. To synchronize a method or block of code you can use synchronized keyword in two different ways:

  1. You can mark a method as synchronized. But you need to know that synchronization works differently in case of static and instance methods. In case of static methods JVM uses monitor that is associated with the class. In case of instance method JVM uses monitor associated with the instance that owns the method.
    public synchronized void instanceMethod() {
        // code    
    }
    
    public static synchronized void staticMethod() {
        // code   
    }
  2. Sometimes you need to synchronize a block of code rather than a whole method. In such case you can use a synchronized block. The way how it works depends on where the block was executed. In static methods you must pass class object. In instance methods use object.
    synchronized (this) {
        // code        
    }
    
    synchronized (Signaling.class) {
        // code        
    }

Volatile variables

When a processor executes instructions we don’t know for sure where exactly variables will be stored. It can store variables either in registers, cache or RAM. As result, if one thread adds variable to the cache and changes it then another thread that is being executed in another processor may not see changes made by the first thread.

The Java keyword volatile garanties visibility of changes to variables that were made across threads. In fact, all volatile variables are stored in RAM hence they are visible for all threads.

Here go examples of the volatile variables.

// Instance variable
public volatile Object obj;

// Static variable
public static volatile Object obj;

Deadlock

Deadlock is a situation when two or more threads are waiting for each other to finish. As result, neither of threads complete. Let’s have a look at the example below. The example contains steps of thread execution.

deadlock

On the image above we have two threads: thread A and thread B. In first step thread A acquire resource 1 and thread B acquire resource 2. In the second step thread A wants to acquire resource 2 but cannot do that since resource 1 is already acquired by thread B. At the same time thread B wants to acquire resource 1 but cannot do that since resource 2 is already acquired by thread A.

Deadlocks occurs when two or more threads acquire thr same resources in different order. To resolve deadlock you can fix the order or you can use timeouts.

Livelock

Another issue that may occur in multithreading application is livelock. It is a situation when threads a not blocked by each other but they are constantly changing state, get stuck and don’t do useful work.

A good way to understand livelock is example when two people meet in a narrow corridor, and each tries to be polite by moving aside to let the other pass, but they end up swaying from side to side without making any progress because they both repeatedly move the same way at the same time.

Thread signaling. wait(), notify() and notifyAll()

Strictly speaking, thread signaling helps to use CPU in efficient way, because when a thread is inactive the CPU doesn’t spent its resources on it. The class java.lang.Object provides three methods wait(), notify(), and notifyAll() that allows to make a thread active/inactive until it receives a signal from another thread.

  1. wait() – causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
  2. notify() – wakes up a single thread that is waiting on this object’s monitor.
  3. notifyAll() – Wakes up all threads that are waiting on this object’s monitor.

The important thing is that all these methods must be executed inside a synchronized block or method that holds lock on the method is called on. Otherwise an IllegalMonitorStateException is thrown.

Here goes the example of thread signaling.

class MyThread extends Thread {

    private Object obj;

    public void setObj(Object obj) {
        this.obj = obj;
    }

    @Override
    public void run() {
        System.out.println("start thread");

        synchronized (obj) {
            try {
                obj.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("end thread");
    }
}

public class Signaling {

    private Object obj = new Object();

    public void runApp() {
        MyThread thread = new MyThread();
        thread.setObj(obj);
        thread.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        synchronized (obj) {
            obj.notify();
        }
    }


    public static void main(String[] args) {
       new Signaling().runApp();
    }

}

On the example above you may notice a strange thing. The wait() and notify() are synchronized on the same object and if after calling wait() method the thread is inactive and locks monitor then how notify() will be called by another thread? The notify() method will be called because whenever the wait() method is called it releases the lock which allows to other threads call notify() and notifyAll() methods. What happens with lock when thread is awakened? It simply tries to obtain the lock again and this is possible only when synchronized block with notify() method completes.

Spurious Wakeups

A waiting thread can be awaken without a signal due to the implementation of the thread interaction mechanism. To prevent spurious wakeups the wait() method can be performed inside inside a loop.

synchronized(object){
    while(!wasSignalled){
        try{
            object.wait();
        } catch(InterruptedException e) {...}
    }
}

sleep()

Static method sleep() pauses current thread for a specified period of time. Thus, CPU can spend its resources on another thread. The method throws InterruptedException if another thread interrupts the current thread while sleep is active. In case of sleep() method the lock is not released.

join()

Pauses current thread and waits until thread on which join() method has been executed is dead. You can specify number of milliseconds plus nanos nanoseconds for this thread to die. The method throws InterruptedException if another thread interrupts the current thread while sleep is active.

yield()

This static method causes the currently executing thread object to temporarily pause and allow other threads to execute. In practice it is never used because there are generally better ways to perform the tasks that you might want to perform with yield().

interrupt(), isInterrupted()  and interrupted()

Interrupt mechanism is just a way for one thread to interrupt another. To mark a thread as interrupted you need to have a instance of that thread and call interrupt() method on it. As result the thread will be marked as interrrupted. Inside of the interrupted method you can use either isInterrupted() or interrupted() methods. The difference is that the interrupted() is a static method that tests whether the current thread has been interrupted. The isInterrupted() method tests whether this thread has been interrupted. Another difference is that interrupted() also resets the status of the current thread.

Trade priority

Threads are always have some priority that is represented as a number between 1
and 10. Thus, the scheduler runs the threads using priority and the threads with the highest priority will be executed first. To set a priority for the thread you can use setPriority() method that takes int values.

Also, the java.lang.Thread class provides three constants:

  1. MIN_PRIORITY – The minimum priority that a thread can have. Equals to 1.
  2. NORM_PRIORITY – The default priority that is assigned to a thread. Equals to 5.
  3. MAX_PRIORITY – The maximum priority that a thread can have. Equals to 10.

Starvation

Starvation is a problem that occurs when some threads swallow all CPU resources and other threads at the same time remain disadvantaged. For example, an application has one thread with priority one and many threads that are constantly created with priority ten. In such case, the thread with the lowest priority has fewer chances to execute rather than threads with highest priority. Another example of starvation that may occur is when we have many threads that are executed by scheduler. The scheduler does not guarantee the order of execution of threads. As result there is a chance that some threads will not be executed for a very long time. Also, the same situation may occur with notify() method that does not guarantee the order of execution of objects.

There many solutions that solve starvation but they are out of scope of this overview. Such solutions called fairness.

Class ThreadLocal

ThreadLocal object allows you to read and write variables that will be only visible by the same thread even if two or more threads have reference on the same ThreadLocal object.

Here go example:

class GoThread extends Thread {

    private ThreadLocal<String> threadLocal;

    public GoThread(ThreadLocal<String> threadLocal) {
        this.threadLocal = threadLocal;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " - " + threadLocal.get());

        threadLocal.set("value thread");

        System.out.println(Thread.currentThread().getName() + " - " + threadLocal.get());
    }

}

public class ThreadLocalExample {

    public void go() {
        ThreadLocal<String> threadLocal = new ThreadLocal<String>();

        threadLocal.set("value main");

        GoThread thread1 = new GoThread(threadLocal);
        thread1.setName("Thread 1");
        thread1.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) { }

        GoThread thread2 = new GoThread(threadLocal);
        thread2.setName("Thread 2");
        thread2.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) { }

        System.out.println(Thread.currentThread().getName() + " - " + threadLocal.get());
    }


    public static void main(String[] args) {
        new ThreadLocalExample().go();
    }

}

Deamon thread

In Java a process completes when all threads are dead. But this is not applicable to the deamon threads. Because when the last thread completes then all deamon threads are are also will be completed.
To create a daemon thread you can use method setDaemon(true) and to test whether a thread is daemon use isDaemon().

P.S. This is Java Concurrency Overview Part 1 were I descrbed basic tools in the next part I’make overview of Java concurrent package.

Leave a Reply