Synchronisation

We use synchronized if we only want 1 Thread to perform the code while the others wait.

Method

We can put the keyword in a method, for example. Then all the thread, whenever this method is executed, it happens 1 thread at a time

public synchronized void calculate(String author) {  
  while (currentNumber < 1000) {  
    currentNumber *= 2;  
    System.out.printf("Current number %s from %s%n", currentNumber, author);  
  }  
}
Running: test1
Running: test2
Running: test3
Current number 2 from test1
Current number 4 from test1
Current number 8 from test1
Current number 16 from test1
Current number 32 from test1
Current number 64 from test1
Current number 128 from test1
Current number 256 from test1
Current number 512 from test1
Current number 1024 from test1

Process finished with exit code 0

This is the same thing as

public void calculate(String author) {  
  synchronized (this) {  
  }
}

Block

We can run using synchronized block to block out a code to run in single thread.

public void calculate(String author) {  
  System.out.printf("Running calculation for %s%n", author);  
  synchronized (this) {  
    while (currentNumber < 1000) {  
      currentNumber *= 2;  
      System.out.printf("Current number %s from %s%n", currentNumber, author);  
    }  
  }  
}
Running: test1
Running: test2
Running calculation for test2
Running: test3
Running calculation for test3
Current number 2 from test2
Current number 4 from test2
Current number 8 from test2
Current number 16 from test2
Current number 32 from test2
Current number 64 from test2
Current number 128 from test2
Current number 256 from test2
Current number 512 from test2
Current number 1024 from test2
Running calculation for test1

So only the calculation block is run in single threaded.

Explaination

In here, we pass in this object in synchronised. This is monitor object. That means only 1 thread at the time can access to this object.

If for example, the method is static, then we are passing the class object instead:

class Calculator {
	public static void calculate(String author) {  
	  System.out.printf("Running calculation for %s%n", author);  
	  synchronized (Calculator.class) {  
	    while (currentNumber < 1000) {  
	      currentNumber *= 2;  
	      System.out.printf("Current number %s from %s%n", currentNumber, author);  
	    }  
	  }  
	}
}

[!note]
For block, we can also choose to synchronise any object or variables. This is because by default, java has an Instinct lock on each of the object.

Synchronisation on value class

For example if we have something like this:

@ThreadSafe
public class Synchronisation {
    private static Integer mutualInteger = 0;

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        IntStream.range(0,5).forEach((i) -> executorService.submit(Synchronisation::process));
        executorService.shutdown();
    }

    public static void process() {
        synchronized (mutualInteger) { // NOTE HERE
            mutualInteger += 1;
            System.out.println("Accessed " + mutualInteger + " from " + Thread.currentThread().getName());
        }
    }
}

Note that in this case we cannot synchronized on value classes (Integer).

[!important]
We cannot synchronize value classes such as Integer, String, …. It's because these values are immutable. And if it's immutable, the state cannot be changed, so the synchronized block cannot acquire instinct lock for this value.

As a result, value classes like Integer, String does not have instinct lock

A way to solve this is to add another Object and Synchronize that object instead:


@ThreadSafe
public class Synchronisation {
    @GuardedBy("lock")
    private static Integer mutualInteger = 0;

    @GuardedBy("lock")
    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        IntStream.range(0,5).forEach((i) -> executorService.submit(Synchronisation::process));
        executorService.shutdown();
    }

    public static void process() {
        System.out.println("In thread: " +  Thread.currentThread().getName());
        synchronized (lock) {
            mutualInteger += 1;
            System.out.println("Accessed " + mutualInteger + " from " + Thread.currentThread().getName());
        }
		
		// wait for lock to be acquired before doing this
        System.out.println("Out thread: " +  Thread.currentThread().getName());
    }
}

In thread: pool-1-thread-1
In thread: pool-1-thread-4
In thread: pool-1-thread-2
In thread: pool-1-thread-5
In thread: pool-1-thread-3
Accessed 1 from pool-1-thread-1
Accessed 2 from pool-1-thread-3
Out thread: pool-1-thread-1
Out thread: pool-1-thread-3
Accessed 3 from pool-1-thread-5
Out thread: pool-1-thread-5
Accessed 4 from pool-1-thread-2
Out thread: pool-1-thread-2
Accessed 5 from pool-1-thread-4
Out thread: pool-1-thread-4