CAS of Java multithreading

Time:2020-11-30

CAS (Compare and Swap)

CAS has three operands: memory value m, expected value E, and update value U. If and only if the memory value m and the expected value E are equal, change the memory value m to u, otherwise nothing will be done.

1. Application scenarios of CAS

CAS is only suitable for situations with less thread conflicts

Typical application scenarios of CAS are as follows:

  • Atomic class
  • Spinlock

1.1 atomic class

Atomic class is the most typical application of CAS in Java.

Let’s start with a common code snippet.

if(a==b) {
    a++;
}

Ifa++Before execution, what if the value of a is modified? Can we still get the expected value? The reason for this problem is that in the concurrent environment, the above code fragment is not atomic operation, which may be tampered with by other threads at any time.

The most classic way to solve this problem is to apply atomic classesincrementAndGetmethod.

public class AtomicIntegerDemo {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        final AtomicInteger count = new AtomicInteger(0);
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    count.incrementAndGet();
                }
            });
        }

        executorService.shutdown();
        executorService.awaitTermination(3, TimeUnit.SECONDS);
        System.out.println("Final Count is : " + count.get());
    }

}

J. The U.C. package providesAtomicBooleanAtomicIntegerAtomicLongFor eachBooleanIntegerLongPerform atomic operations, which are similar to the above examples and will not be discussed in detail.

1.2 spinlock

Spin locking can be realized by using atomic class (CAS in essence).

The so-called spin lock means that the thread repeatedly checks whether the lock variable is available until it succeeds. Since the thread keeps executing during this process, it is a busy wait. Once a spinlock is acquired, the thread holds the lock until it is explicitly released.

Example: non thread safe example

public class AtomicReferenceDemo {

    private static int ticket = 10;

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 5; i++) {
            executorService.execute(new MyThread());
        }
        executorService.shutdown();
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            while (ticket > 0) {
                System.out.println ( Thread.currentThread (). Getname() + "sold" + ticket + "ticket");
                ticket--;
            }
        }

    }

}

Output results:

Pool-1-thread-2 sold the 10th ticket
Pool-1-thread-1 sold the 10th ticket
Pool-1-thread-3 sold the 10th ticket
Pool-1-thread-1 sold the eighth ticket
Pool-1-thread-2 sold the ninth ticket
Pool-1-thread-1 sold the sixth ticket
Pool-1-thread-3 sold the seventh ticket
Pool-1-thread-1 sold the fourth ticket
Pool-1-thread-2 sold the fifth ticket
Pool-1-thread-1 sold the second ticket
Pool-1-thread-3 sold the third ticket
Pool-1-thread-2 sold the first ticket

It is obvious that there has been a repeat sale of tickets.

[example] using spin lock to ensure thread safety

Thread safety can be ensured by using the non blocking synchronization of spin lockAtomicReferenceTo implement a spin lock.

public class AtomicReferenceDemo2 {

    private static int ticket = 10;

    public static void main(String[] args) {
        threadSafeDemo();
    }

    private static void threadSafeDemo() {
        SpinLock lock = new SpinLock();
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 5; i++) {
            executorService.execute(new MyThread(lock));
        }
        executorService.shutdown();
    }

    static class SpinLock {

        private AtomicReference atomicReference = new AtomicReference<>();

        public void lock() {
            Thread current = Thread.currentThread();
            while (!atomicReference.compareAndSet(null, current)) {}
        }

        public void unlock() {
            Thread current = Thread.currentThread();
            atomicReference.compareAndSet(current, null);
        }

    }

    static class MyThread implements Runnable {

        private SpinLock lock;

        public MyThread(SpinLock lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            while (ticket > 0) {
                lock.lock();
                if (ticket > 0) {
                    System.out.println ( Thread.currentThread (). Getname() + "sold" + ticket + "ticket");
                    ticket--;
                }
                lock.unlock();
            }
        }

    }

}

Output results:

Pool-1-thread-2 sold the 10th ticket
Pool-1-thread-1 sold the ninth ticket
Pool-1-thread-3 sold the eighth ticket
Pool-1-thread-2 sold the seventh ticket
Pool-1-thread-3 sold the sixth ticket
Pool-1-thread-1 sold the fifth ticket
Pool-1-thread-2 sold the fourth ticket
Pool-1-thread-1 sold the third ticket
Pool-1-thread-3 sold the second ticket
Pool-1-thread-1 sold the first ticket

2. CAS principle

Java mainly usesUnsafeCAS operations provided by this class.UnsafeCAS depends on the hardware instructions implemented by the JVM for different operating systemsAtomic::cmpxchgAtomic::cmpxchgThe implementation of the system uses the assembly CAS operation, and uses thelockThe signal guarantees its atomicity.

3. Problems brought by CAS

In general, CAS performs better than locking. Because CAS is a non blocking algorithm, it avoids the waiting time of thread blocking and wake-up.

However, there are always advantages and disadvantages in CAS

  • ABA problem
  • Long cycle time and high cost
  • Only one shared variable can be guaranteed atomicity

How to solve these three problems:

3.1 ABA problem

If a variable is first read with a value, its value is changed to B, and then it is changed back to a, the CAS operation will mistakenly think that it has never been changed

J. A package with a tag is providedAtomic reference classes are as follows:AtomicStampedReferenceTo solve this problemIt can ensure the correctness of CAS by controlling the version of variable value. In most cases, ABA problem will not affect the correctness of program concurrency. If you need to solve ABA problem, useTraditional mutex synchronization may be more efficient than atomic classes
Solution: add flag bits, such as atomicmarkablereference, atomicstampedreference

3.2 long cycle time and high cost

Spinning CAS (keep trying until successful) if it fails for a long time, it will bring huge execution cost to CPU

If the JVM can support thepauseCommand, then the efficiency will be improved to some extent,pauseInstructions have two functions:

  • It can delay the pipeline execution so that the CPU does not consume too much execution resources. The delay time depends on the implementation version, and the delay time is zero on some processors.
  • It can avoid the CPU pipeline flushing caused by memory order violation when exiting the loop, so as to improve the CPU execution efficiency.

Solution: because it is a while loop, the consumption is bound to be large. Set the maximum number of attempts

3.3 only one shared variable can be guaranteed atomicity

When performing operations on a shared variable, we can use cyclic CAS to ensure atomic operations. However, when operating on multiple shared variables, cyclic CAS cannot guarantee the atomicity of operations, so locks can be used.

Or there is a clever way, is to merge multiple shared variables into a shared variable to operate. For example, there are two shared variablesi = 2, j = a, merge themij=2aAnd then operate with CASij。 Starting with Java 1.5, JDK providesAtomicReferenceClass to ensure atomicity between reference objects
Solution: use atomicreference to encapsulate multiple variables into an object for CAS operation

Pay attention to the official account: Java treasure
a