Java Multi Threading : Synchronize, Atomic, Volatile

wahyu eko hadi saputro
5 min readApr 24, 2020

--

On this time, I will write what I have learned about java keyword like volatile, atomic for example AtomicInteger, and Synchronize. Actually I write this post, base on my understanding and my opinion after reading some articles and books.

In my opinion the hard part of java language programming is multithreading. Multithreading is we spawn many thread to process some tasks. Base on the definition, we have conclusion that we can process some tasks faster than without multithreading, but multithreading has some drawbacks like race condition, hard to debug etc.

Some terms will be explained following:

1. Race condition : race condition is a condition where a value / object inside memory is manipulated by many threads. For example, you have a box, then you fill the box with football, volleyball, basket ball alternately. Box is memory, and football, volleyball, basket ball are representation of 3 threads.

Example :

public class ThreadCounter implements Runnable {private int counter;

public void increment() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
counter++;
}
public void decrement() {
counter--;
}
public int getValue() {
return counter;
}
public void run() {
// TODO Auto-generated method stub
this.increment();
System.out.println("Value After increment " + Thread.currentThread().getName() + " = " + this.getValue());
// decrementing
this.decrement();
System.out.println("Last value of thread " + Thread.currentThread().getName() + " = " + this.getValue());
}}public class MainRace {public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadCounter counter = new ThreadCounter();
Thread t1 = new Thread(counter, "Thread-1");
Thread t2 = new Thread(counter, "Thread-2");
Thread t3 = new Thread(counter, "Thread-3");
Thread t4 = new Thread(counter, "Thread-4");
Thread t5 = new Thread(counter, "Thread-5");
Thread t6 = new Thread(counter, "Thread-6");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}

Based on code above, the code instantiate ThreadCounter once and then creates thread to execute the instance. Thus, one instance is processed by 6 threads. Class ThreadCounter has instance variable ‘counter’ and the counter variable will be manipulated by 6 threads, therefore counter value will be vary.

Now, how to handle race condition ? my first thought and the simplest is creating new instance for 1 thread. You assign one instance to one thread.

public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadCounter counter = new ThreadCounter(); //new object
ThreadCounter counter1 = new ThreadCounter(); //new object
ThreadCounter counter2 = new ThreadCounter(); //new object
ThreadCounter counter3 = new ThreadCounter(); //new object
ThreadCounter counter4 = new ThreadCounter(); //new object
Thread t1 = new Thread(counter, "Thread-1");
Thread t2 = new Thread(counter1, "Thread-2");
Thread t3 = new Thread(counter2, "Thread-3");
Thread t4 = new Thread(counter3, "Thread-4");
Thread t5 = new Thread(counter4, "Thread-5");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}

2. Synchronize keyword : Synchronize keyword has functionality to make sure only one thread accessing the instance class or class at once time. Under the hood synchronize will perform locking to the class or instance class if one thread is accessing. if one thread enter synchronize instance method it will lock instance class. if one thread enter synchronize static method it will lock class.

class SynchronizeObject implements Runnable{private int counter;

public void increment() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
counter ++;
}
public void decrement() {
counter --;
}
public int getValue() {
return counter;
}
public synchronized void run() {
// TODO Auto-generated method stub
this.increment();
System.out.println("Value After increment " + Thread.currentThread().getName() + " = " + this.getValue());
// decrementing
this.decrement();
System.out.println("Value for Thread at last " + Thread.currentThread().getName() + " = " + this.getValue());
}}public class SynchronizeThread {
public static void main(String[] args) {
// TODO Auto-generated method stub
SynchronizeObject counter = new SynchronizeObject();
Thread t1 = new Thread(counter, "SynchronizeThread-1");
Thread t2 = new Thread(counter, "SynchronizeThread-2");
Thread t3 = new Thread(counter, "SynchronizeThread-3");
Thread t4 = new Thread(counter, "SynchronizeThread-4");
Thread t5 = new Thread(counter, "SynchronizeThread-5");
Thread t6 = new Thread(counter, "SynchronizeThread-6");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}

3. Atomic : java has package for concurrency named “java.util.concurrent.atomic”, in the atomic package all methods of classes are atomic (thread safe). For illustration you have operation x = x +1, where initial value of x = 0, if the operation is atomic, you will get value of x either before operation or after operation. Atomic operation is best for thread counter.

class AtomicThread implements Runnable{private AtomicInteger counter = new AtomicInteger(0);

public void increment() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
counter.getAndIncrement();
}
public void decrement() {
counter.getAndDecrement();
}
public AtomicInteger getValue() {
return counter;
}
public void run() {
// TODO Auto-generated method stub
this.increment();
System.out.println("Value After increment " + Thread.currentThread().getName() + " = " + this.getValue());
// decrementing
this.decrement();
System.out.println("Value for Thread at last " + Thread.currentThread().getName() + " = " + this.getValue());
}}public class ThreadAtomic {
public static void main(String[] args) {
// TODO Auto-generated method stub
AtomicThread counter = new AtomicThread();
Thread t1 = new Thread(counter, "AtomicThread-1");
Thread t2 = new Thread(counter, "AtomicThread-2");
Thread t3 = new Thread(counter, "AtomicThread-3");
Thread t4 = new Thread(counter, "AtomicThread-4");
Thread t5 = new Thread(counter, "AtomicThread-5");
Thread t6 = new Thread(counter, "AtomicThread-6");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}

The code has counter variable with type AutomaticInteger. AutomaticInteger has atomic methods for example getAndIncrement or getAndDecrement. The methods of AutomaticInteger are thread safe.

4. Volatile keyword : volatile means a value in memory is visible to all thread. Volatile keyword makes sure that all threads get value of the variable from main memory. Each thread directly accesses main memory. Volatile keyword is hard to explain and to proof because it is related to memory management. But using volatile keyword only is not enough for avoiding race condition, we should combine with synchronize keyword to avoid race condition.

package com.thread.volatilevar;class VolatileThread implements Runnable{private volatile int counter;

public void increment() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
counter++;
}
public void decrement() {
counter--;
}
public int getValue() {
return counter;
}
public void run() {
// TODO Auto-generated method stub
this.increment();
System.out.println("Value After increment " + Thread.currentThread().getName() + " = " + this.getValue());


// decrementing
this.decrement();
System.out.println("Value for Thread at last " + Thread.currentThread().getName() + " = " + this.getValue());
}}public class ThreadVolatile {
public static void main(String[] args) {
// TODO Auto-generated method stub
VolatileThread counter = new VolatileThread();
Thread t1 = new Thread(counter, "Thread-1");
Thread t2 = new Thread(counter, "Thread-2");
Thread t3 = new Thread(counter, "Thread-3");
Thread t4 = new Thread(counter, "Thread-4");
Thread t5 = new Thread(counter, "Thread-5");
Thread t6 = new Thread(counter, "Thread-6");
t1.start();
t2.start();

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

t3.start();
t4.start();

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t5.start();
t6.start();
}
}

Github : https://github.com/wahyueko22/java-multithreading

--

--

No responses yet