Java's Happens-Before, Threads & Concurrency Introduction

Multi-threading and Concurrency Intro

This is a short explainer behind the syntax and the need for Concurrency and Multi-threading APIs given to us by Oracle's Java.1

Why Concurrency?

Before concurrency and multi-threading was about (way before my time), code used to execute sequentially. One line at a time. This doesn't allow for complex tasks - such as displaying amd manipulating Stock Market Data coming from more than one source - to be handled simultaneously. Certain operations that can be done independently of one another, can be run 'concurrently' in such way that they do not corrupt data and information. For example;

Consider your browser that you are currently - but unlikely - using to read my blog. The browser would be doing more than one thing at a time. Loading the page is one function, but in the background, Chrome's GUI are reacting to the loading mechanism as well as, say if you also open another tap. More close to home, when you type your email while typing, in the browser or MS Word, another process (thread) is checking for spelling mistakes.

These examples of why concurrency is important. Executing tasks side by side to accomplish more than one task to form an application that is quick, efficient, and in the case of GUIs, responsive.

What's in this blog?

So now you have an idea of what concurrency is, we will consider one of two APIs provided by Java. The Runnable interface and the executors, which is out side the scope of this blog.

In the next section will discuss the Runnable interface, synchronized blocks -- although that is a topic of it's own -- and quickly touch on volatile keyword. And end with a big bang and bring it all home and wrap our heads around Java's Happens-Before link.

Let the games begin!

Thread Vs Runnable Object.

      A thread is a process that execute a task. The task needs to implements the Runnable interface and get passed to the thread for execution.

public class ThreadVsRunnable {

    //Step 1; Create a runnable object
    static class ARTask implements Runnable {
        @Override
        public void run() {
            System.out.println("This is our first Task, Yahoo!!");
        }
    }

    public static void main(String[] args) throws InterruptedException {

        //Start Main
        System.out.println("Main thread is Starts here ----- ");

        //Step 2; create a thread and pass a new instance of it to the thread
        Thread myThread = new Thread(new ARTask());
        //Step 3; Start the thread by calling Start (Do not call the run method)
        myThread.start();
        //Step 4; optional step to tell the main thread to wait for this to finish.
        myThread.join();

        //Finish main
        System.out.println("Main thread is finished here ----- ");
    }
}

Another way of creating ad-hoc Runnable objects either by using anonymous classes (Pre Java 8) or using Java 8 Lambdas as follow:

public static void main(String[] args) throws InterruptedException {

        //Start Main
        System.out.println("Main thread is Starts here ----- ");

        //Step 2; create a thread and pass a new instance of it to the thread
        Thread myAnonymousThread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("I am calling from anonymous thread, Oh Boo!!! ");
            }
        });

        Runnable lambdaRunnable = () -> System.out.println("I am running from Lambda thread, Oh Ya!!");
        Thread myLambdaThread = new Thread(lambdaRunnable);
        //Step 2; ------------------------------------------------------------------

        //Step 3; Start the thread by calling Start (Do not call the run method)
        myAnonymousThread.start();
        myLambdaThread.start();

        //Step 4; optional step to tell the main thread to wait for this to finish.
        myAnonymousThread.join();
        myLambdaThread.join();

        //Finish main
        System.out.println("Main thread is finished here ----- ");
    }

And the output as follow:

Main thread is Starts here -----  
I am calling from anonymous thread, Oh Boo!!!  
I am running from Lambda thread, Oh Ya!!  
Main thread is finished here -----  

You could also force the start of a Runnable from it's own constructor when extending the Thread class. This is useful when using for fire-and-forget threads.

public class ThreadVsRunnable {

    //Step 1; Create a runnable object
    static class ARTask extends Thread implements Runnable {

        ARTask() {
            this.start();
        }

        @Override
        public void run() {
            System.out.println("Extending Thread, No need to start me... !!");
        }
    }

    public static void main(String[] args) throws InterruptedException {

        //Start Main
        System.out.println("Main thread is Starts here ----- ");

        //Step 2 & 3;
        new ARTask();

        //Finish main
        System.out.println("Main thread is finished here ----- ");
    }
}

Synchronized Blocks & Volatility Members.

      The reason behind the usefulness of concurrency is the fact things can be done quickly and it follows the simple saying 'Two heads better than one'. However sometimes those two heads (Threads) need to access the same data, variable or class member. If not careful, data can get corrupted faster than you can count to 4. (Trust me I tried)

For example, in the following code we have a simple counter scenario. One counter incremented by two different threads 1000 each. The result, should be 2000 and maybe if you run it enough times, it will be 2000 but no guarantee what so ever that this is going to be the case.

public class ThreadVsRunnable {

    //Step 1; Create a runnable object
    static class Counter {
        private int counter;

        Counter(int counter) {
            this.counter = counter;
        }

        public void inc() {
            this.counter += 1;
        }

        public int getCount() {
            return counter;
        }
    }

    public static void main(String[] args) throws InterruptedException {

        //Start Main
        System.out.println("Main thread Starts here ----- ");

        Counter c = new Counter(0);
        //Increment 4 times.
        Runnable inc = () -> {
            for (int i = 0; i < 1_000; i++) {
                c.inc();
            }
        };

        //Increment 4 times.
        Runnable inc2 = () -> {
            for (int i = 0; i < 1_000; i++) {
                c.inc();
            }
        };

        //Step 2 & 3;
        Thread t1 = new Thread(inc);
        Thread t2 = new Thread(inc2);

        t1.start();
        t2.start();

        t2.join();
        t1.join();

        //Finish main
        System.out.println("Main thread finished here ----- & Counter is: " + c.getCount());
    }
}

After few runs I got 1793, 1752, and 1710. There is no telling of the output. This is because of a problem caused by reading values that are not the latest due to lack of something called Visibility. The reader does not have the latest value from the CPU cashe which the writer's final value has written, instead, it might fetch the original value from RAM or anothet from different cache. Now that we know unprotected concurrency can be harmful, let's go over few important terms;

  • Race Condition: as we have saw in the previous example, race condition is when two or more threads trying to access the same resource with no way of telling who can access it or who has it already! Race condition is the main reason behind resources' corruption.
  • Lock: Each object in java have something called Lock. This lock is used in concurrency to dictates a system whereby the lock is used to access a member and released when it's finished.
  • Deadlock: When we have two threads, T1 & T2. T1 have S1's lock and waiting on S2. And T2 have S2's lock and waiting on S1's lock. This is a deadlock because there is nothing we can do to resolve it unless we terminate the application.
  • ReentrantLock: When a thread already have certain object lock, if the same lock is used to access other resources, the thread is allowed to access it.

So, now we got all that out of the window, let's look at how we can protect our previous code from the Race Condition. Simple add Synchronized keyword

public synchronized void inc() {  
            this.counter += 1;
        }

With the Synchronized keyword, there is three level of scopes;

  • When used with Static, the lock is the class itself not it's instance.
  • When used on non-static members, the lock is the instance of that class.
  • However, the preferred method is to use a special lock, and that can simply be done by creating an Object instance and using it as the key, see below;
private final Object lock = new Object();  
public void inc() {  
            synchronized(lock)
            {
                this.counter += 1;
            }
        }

Finally, the use of the keyword volatile would guarantee for the READER ONLY to get the latest value of a certain object. However synchronized blocks are still required if we need concurrent write operations.

This mechanism is provided to us by the Java APIs if we use the above code correctly. It guarantees the *visibility* of the variable to the reader as it last written value.

What does that mean? you ask! To get slightly in-depth, today's CPU's include caches that holds data that the CPU reads from and write to, instead of the old traditional reading directly from RAM. if the code is correctly synchronized or the variable is set correctly to volatile, Java guarantees a happens-before line between the last write operation to any reader who wishes to see the value of the variable.

Conclusion

This topic was secretly about Variable Visibility and how to protect your vars from being corrupted when working with concurrent systems. The topic is complex and wide, any feedback is appreciated.

  1. By all means I am no expert on the matter and only write these blogs to keep my mind sharp and as a good practice to help me understand the subject further. If you believe I have misinformed or mistaken please reply to me and help me understand more.

AbdulRehman Faraj

Electronic and Computer Engineer graduate and currently working as Development and Integration Analyst at Baring Asset Management. Love Programming, and game development....

London, United Kingdom http://github.com/AbdulR3hman