Concurrent Programming – [ Keywords ]

In Java, there are 3 keywords tightly linked to multi-thread concurrent programming :

  • volatile;
  • synchronized;
  • final.

What You Need

  • About 10 minutes
  • A favorite text editor or IDE
  • Java 8 or later

1. Volatile

Volatile keyword is used to mark a variable.

Every read of a volatile variable will be read from the computer’s main memory, and not from the CPU cache.

Every write to a volatile variable will be written to main memory, and not just to the CPU cache.

It is a solution of solving visibility problem caused by CPU caches in multi-threading context.

And also the solution to solve orderness problem caused by instructions reordering.

When and How to use volatile keyword ?

In general, volatile variable is used as a boolean status flag to indicate that an important one-time event has occurred such as completion of initialization or completion of a process.

import java.util.concurrent.TimeUnit;

public class VolatileExample {
    private static volatile boolean stop;

    public static void main(String[] args) throws InterruptedException {
        Runnable r = () -> {
            while (!stop) {
                System.out.println(Thread.currentThread().getName());
                Thread.yield();
            }
        };

        Thread t = new Thread(r);

        t.start();

        int n = 1;

        TimeUnit.MILLISECONDS.sleep(n);

        stop = true;

        System.out.println("Thread has been signaled to stop after waiting for " + n + " milliseconds.");
    }
}

Below is the output of above code snippet :

Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread has been signaled to stop after waiting for 1 milliseconds.

In above example, if stop is not marked as volatile, there is risk of hanging forever because of the lack of memory visibility.

The second common usage of volatile is double-checked singleton pattern.

public class DoubleCheckSinglton {
    private static volatile DoubleCheckSinglton instance;

    public static DoubleCheckSinglton getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckSinglton.class) {
                if (instance == null) {
                    instance = new DoubleCheckSinglton();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        DoubleCheckSinglton i1 = DoubleCheckSinglton.getInstance();
        DoubleCheckSinglton i2 = DoubleCheckSinglton.getInstance();

        System.out.println("i1 == i2 : " + (i1 == i2));

    }
}

The output of above code snippet is below :

i1 == i2 : true

2. Synchronized

Synchronized keyword is used to mark a block of code.

When using a synchronized block, java internally uses a monitor to provide synchronization.

These monitors are bound to an object, therefore, all synchronized blocks of the same object can have only one thread executing them at the same time.

2.1 Synchronized Instance Methods

We can add the synchronized keyword in the method declaration to make the method synchronized.

In this case, instance methods are synchronized over the instance of the class owning the method, which means only one thread per instance of the class can execute this method.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class SynchronizedOnInstanceMethod {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(10);

        Counter counter = new Counter();

        for (int i = 0; i < 1000; i++) {
            service.submit(counter::inc);
        }

        service.shutdown();

        service.awaitTermination(10, TimeUnit.SECONDS);

        System.out.println("sum = " + counter.sum);
    }

    private static class Counter {
        private int sum = 0;

        synchronized void inc() {
            this.sum++;
        }
    }
}

Below is the output of above code snippet :

sum = 1000

From the thread dump we can see that the threads are waiting for monitor on SynchronizedOnInstanceMethod$Counter object :

"pool-1-thread-10" #31 [7766] prio=5 os_prio=0 cpu=0,66ms elapsed=32,33s tid=0x000078d1781c05b0 nid=7766 waiting for monitor entry  [0x000078d14c7fe000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at SynchronizedOnInstanceMethod$Counter.inc(SynchronizedOnInstanceMethod.java:26)
        - waiting to lock <0x0000000716417f48> (a SynchronizedOnInstanceMethod$Counter)
        at SynchronizedOnInstanceMethod$$Lambda/0x000078d100000c08.run(Unknown Source)
        at java.util.concurrent.Executors$RunnableAdapter.call(java.base@21.0.3/Executors.java:572)
        at java.util.concurrent.FutureTask.run(java.base@21.0.3/FutureTask.java:317)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.3/ThreadPoolExecutor.java:1144)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.3/ThreadPoolExecutor.java:642)
        at java.lang.Thread.runWith(java.base@21.0.3/Thread.java:1596)
        at java.lang.Thread.run(java.base@21.0.3/Thread.java:1583)

2.2 Synchronized Static Methods

If synchronized keyword is added in a static method, then it is synchronized on the class object associated with the class.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class SynchronizedOnStaticMethod {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(10);

        for (int i = 0; i < 1000; i++) {
            service.submit(Counter::inc);
        }

        service.shutdown();

        service.awaitTermination(10, TimeUnit.SECONDS);

        System.out.println("sum = " + Counter.sum);
    }

    private static class Counter {
        private static int sum = 0;

        static synchronized void inc() {
            sum++;
        }
    }
}

The output of above code snippet is below :

sum = 1000

From the thread dump we can see that the threads are waiting for monitor on SynchronizedOnInstanceMethod$Counter’s class object :

"pool-1-thread-10" #31 [9743] prio=5 os_prio=0 cpu=0,43ms elapsed=17,31s tid=0x00007442a41bad80 nid=9743 waiting for monitor entry  [0x00007442883ae000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at SynchronizedOnStaticMethod$Counter.inc(SynchronizedOnStaticMethod.java:24)
        - waiting to lock <0x000000071641ab70> (a java.lang.Class for SynchronizedOnStaticMethod$Counter)
        at SynchronizedOnStaticMethod$$Lambda/0x000074422c000c00.run(Unknown Source)
        at java.util.concurrent.Executors$RunnableAdapter.call(java.base@21.0.3/Executors.java:572)
        at java.util.concurrent.FutureTask.run(java.base@21.0.3/FutureTask.java:317)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.3/ThreadPoolExecutor.java:1144)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.3/ThreadPoolExecutor.java:642)
        at java.lang.Thread.runWith(java.base@21.0.3/Thread.java:1596)
        at java.lang.Thread.run(java.base@21.0.3/Thread.java:1583)

2.3 Synchronized Blocks Within Methods

When applying synchronized to a block of code, a monitor object should be passed to this block.

The code inside the block gets synchronized on the monitor object.

Only one thread per monitor object can execute inside that block of code.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class SynchronizedOnBlockOfCode {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(10);

        Counter counter = new Counter();

        MyLock lock = new MyLock();

        for (int i = 0; i < 1000; i++) {
            service.submit(() -> {
                synchronized (lock) {
                    counter.inc();
                }
            });
        }

        service.shutdown();

        service.awaitTermination(10, TimeUnit.SECONDS);

        System.out.println("sum = " + counter.sum);
    }

    private static class Counter {
        private int sum = 0;

        void inc() {
            this.sum++;
        }
    }

    private static class MyLock {
    }
}

Below is the output of above code snippet :

sum = 1000

From the thread dump we can see that the threads are waiting for monitor on SynchronizedOnBlockOfCode$MyLock object :

"pool-1-thread-10" #31 [14126] prio=5 os_prio=0 cpu=0,43ms elapsed=132,28s tid=0x00007d26281b8730 nid=14126 waiting for monitor entry  [0x00007d25b522e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at SynchronizedOnBlockOfCode.lambda$0(SynchronizedOnBlockOfCode.java:16)
        - waiting to lock <0x0000000716418ba0> (a SynchronizedOnBlockOfCode$MyLock)
        at SynchronizedOnBlockOfCode$$Lambda/0x00007d25b8001000.run(Unknown Source)
        at java.util.concurrent.Executors$RunnableAdapter.call(java.base@21.0.3/Executors.java:572)
        at java.util.concurrent.FutureTask.run(java.base@21.0.3/FutureTask.java:317)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21.0.3/ThreadPoolExecutor.java:1144)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21.0.3/ThreadPoolExecutor.java:642)
        at java.lang.Thread.runWith(java.base@21.0.3/Thread.java:1596)
        at java.lang.Thread.run(java.base@21.0.3/Thread.java:1583)

2.4 Reentrancy

The lock behind the synchronized methods and the blocks are re-entrant.

The thread can acquire the same synchronized lock over and over again while holding it.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class SynchronizedReentrancy {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(10);

        Counter counter = new Counter();

        for (int i = 0; i < 1000; i++) {
            service.submit(() -> {
                synchronized (counter) {
                    // First time acquiring the lock
                    synchronized (counter) {
                        // I already have the lock
                        // so i can enter here without any issue
                        counter.inc();
                    }
                }
            });
        }

        service.shutdown();

        service.awaitTermination(10, TimeUnit.SECONDS);

        System.out.println("sum = " + counter.sum);
    }

    private static class Counter {
        private int sum = 0;

        void inc() {
            this.sum++;
        }
    }
}

The output of above code snippet is below :

sum = 1000

3. Final

Final keyword is applicable to a variable, a method, or a class.

3.1 Final Classes

Classes marked as final cannot be extended (inherited).

public class FinalClasses {
    private static final class A {

    }

    private static class B extends A {

    }
}

Above code snippet will cause a compilation error :

ovo@ovo:~/github/BlogTests$ javac java.juc/FinalClasses.java
java.juc/FinalClasses.java:6: error: cannot inherit from final A
    private static class B extends A {
                                   ^
1 error

There are two uses of a final class :

  • definitely to prevent inheritance, as final classes cannot be extended.
    For example, all Wrapper Classes like Integer, Float, etc are final classes;
  • to create an immutable class like the predefined String class.

3.2 Final Methods

Methods marked as final cannot be overridden.

public class FinalMethods {
    private static class Person {
        public final void greet() {
            System.out.println("Hello!");
        }
    }

    private static class Employee extends Person {
        @Override
        public final void greet() {
            System.out.println("Hi!");
        }
    }
}

Above code snippet will cause a compilation error :

ovo@ovo:~/github/BlogTests$ javac java.juc/FinalMethods.java
java.juc/FinalMethods.java:10: error: greet() in Employee cannot override greet() in Person
        public final void greet() {
                          ^
  overridden method is final
1 error

The main usage of final method consist of when we don’t need to prohibit a class extension entirely, but only prevent overriding of some methods.

3.3 Final Variables

Variables marked as final can’t be reassigned (Once it is initialized, it can’t be altered).

3.3.1 Final Primitive Variables
public class FinalPrimitiveVariables {
    public static void main(String[] args) {
        final int x = 10;
        
        x = 11;
    }
}

Above code snippet will cause a compilation error :

ovo@ovo:~/github/BlogTests$ javac java.juc/FinalPrimitiveVariables.java
java.juc/FinalPrimitiveVariables.java:5: error: cannot assign a value to final variable x
        x = 11;
        ^
1 error
3.3.2 Final Reference Variables

A final reference variable cannot be reassigned neither.

public class FinalReferenceVariable {
    public static void main(String[] args) {
        final Person person = new Person("tom");
        person = new Person("jerry");
    }

    private static class Person {
        private String name;

        public Person(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

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

Above code snippet will cause a compilation error :

ovo@ovo:~/github/BlogTests$ javac java.juc/FinalReferenceVariable.java 
java.juc/FinalReferenceVariable.java:4: error: cannot assign a value to final variable person
        person = new Person("jerry");
        ^
1 error

But it doesn’t mean that the object it refers to is immutable.

The properties of this object can be changed freely.

public class FinalReferenceVariable {
    public static void main(String[] args) {
        final Person person = new Person("tom");
        System.out.println(person.name);
        person.name = "jerry";
        System.out.println(person.name);
    }

    private static class Person {
        private String name;

        public Person(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

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

Below is the output of above code snippet :

tom
jerry
3.3.3 Constants

Constant is a value that cannot be changed after assigning it.

Java does not directly support the constants.

To declare any variable as constant, we use static and final modifiers.

It is also known as non-access modifiers.

According to the Java naming convention the identifier name must be in capital letters.

public class Constants {
    public static final String GREETING = "Hello, World!";

    public static void main(String[] args) {
        System.out.println(GREETING);
    }
}

Below is the output of above code snippet :

Hello, World!