Compare And Swap (Cas)

Use of Atomic operation that is supposed to execute in a single thread only as an atomic.

Mainly have 2 pattern

1. Blocking critical code section

Similar to Synchronisation but put more resources on the CPU

package multithreads;

import java.sql.SQLOutput;
import java.time.Instant;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.IntStream;

public class AtomicBooleanDemo {
  public static void main(String[] args) throws InterruptedException {
    AtomicBoolean atomicLock = new AtomicBoolean(false);
    CountDownLatch countDownLatch = new CountDownLatch(5);
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    IntStream.range(0, 5).forEach(i -> {
      executorService.submit(() -> {
        criticalSection(atomicLock);
        countDownLatch.countDown();
      });
    });
    countDownLatch.await();
    executorService.shutdown();
  }

  private static void criticalSection(AtomicBoolean atomicLock) {
    while (!atomicLock.compareAndSet(false, true)) { 
	    // check if atomic lock is false. If it is then set to true
	    // which will make other thread keep waiting for it to be false
    };

    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
    System.out.printf("Executed by %s %s%n", Thread.currentThread().getName(), Instant.now());
    atomicLock.set(false);
  }
}

Executed by pool-1-thread-2 2023-08-23T10:09:19.804320Z
Executed by pool-1-thread-4 2023-08-23T10:09:21.810309Z
Executed by pool-1-thread-5 2023-08-23T10:09:23.814312Z
Executed by pool-1-thread-3 2023-08-23T10:09:25.817698Z
Executed by pool-1-thread-1 2023-08-23T10:09:27.822849Z

2. Optimistic locking

Perform the same way as normal locking using Mutex or synchronised block.

However, in this case, we allows multiple thread to go in, but only take 1 commit.

package multithreads;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.IntStream;

public class AtomicLongDemo {
  public static void main(String[] args) throws InterruptedException {
    AtomicLong atomicLong = new AtomicLong(0);
    CountDownLatch countDownLatch = new CountDownLatch(3);
    ExecutorService executorService = Executors.newFixedThreadPool(3);

    IntStream.range(0, 3).forEach(i -> {
      executorService.submit(() -> {
        optimisticLock(atomicLong);
        countDownLatch.countDown();
      });
    });

    executorService.shutdown();
    countDownLatch.await();

    System.out.println("final value: " + atomicLong.get());
  }

  public static void optimisticLock(AtomicLong atomicLong) {
    boolean increaseSuccessful = false;

    while (!increaseSuccessful) {
      long oldValue = atomicLong.get();
      increaseSuccessful = atomicLong.compareAndSet(oldValue, oldValue + 1);
      if (increaseSuccessful) {
        System.out.printf("Increased successfully from %s -> %s Thread %s%n",
            oldValue,
            oldValue + 1,
            Thread.currentThread().getName());
      }
    }
  }
}

See CAS Pattern