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