Thread safety

A class is thread-safe if it behaves the right way when multiple threads access it, regardless of the scheduler or alternate execution of the processes, and does not require any synchronization or coordination in the calling code.

atomic

Provides mutually exclusive access, with only one thread accessing it at a time.

Atomic package

Located in Java. Util. Concurrent atomic, AtomicXXX: CAS pareAndSwapXXX, Unsafe.com

Compare and Swap (CAS) is a technique used to design concurrency algorithms. Compare and replace is to Compare an expected value with the current value of a variable, and if the value of the variable is equal to the expected value, then replace the variable with a new value.

case

Thread safety

package com.keytech.task; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.LongAdder; /** * @className: AtomicTest * @description: TODO class description * @author: MAC * @date: 2020/12/27 **/ public class AtomicTest { private static Integer clientTotal=5000; private static Integer threadTotal=200; private static AtomicInteger count=new AtomicInteger(0); public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); Semaphore semaphore= new Semaphore(threadTotal); for (int i = 0; i <clientTotal ; i++) { executorService.execute(()->{ try{ semaphore.acquire(); update(); semaphore.release(); }catch (Exception e){ e.printStackTrace(); }}); } executorService.shutdown(); System.out.println("count:"+count); } private static void update(){ count.incrementAndGet(); }}Copy the code

GetAndAddInt source

public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(! this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }Copy the code

CompareAndSwapInt (This, stateOffset, Expect, Update) uses cas technology to predict whether the initial value of the stateOffset variable is Expect. If it is, If not, rotate until the initial value of stateOffset is Expect, and then change the value of stateOffset to update

LongAddr

Thread safety

package com.keytech.task; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.LongAdder; /** * @className: LongAddrTest * @description: TODO class description * @author: MAC * @date: 2020/12/28 **/ public class LongAddrTest { private static Integer clientTotal=5000; private static Integer threadTotal=200; private static LongAdder count=new LongAdder(); public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); Semaphore semaphore=new Semaphore(threadTotal); for (int i = 0; i < clientTotal; i++) { try{ semaphore.acquire(); update(); semaphore.release(); }catch (Exception e){ e.printStackTrace(); } } executorService.shutdown(); System.out.println("count"+count); } private static void update(){ count.increment(); }}Copy the code

AtomicLong

Thread safety

package com.keytech.task; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicLong; /** * @className: AtomicLongTest * @description: TODO class description * @author: MAC * @date: 2020/12/28 **/ public class AtomicLongTest { private static Integer clientTotal=5000; private static Integer threadTotal=200; private static AtomicLong count=new AtomicLong(); public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); Semaphore semaphore=new Semaphore(threadTotal); for (int i = 0; i < clientTotal; i++) { try{ semaphore.tryAcquire(); update(); semaphore.release(); }catch (Exception e){ e.printStackTrace(); } } executorService.shutdown(); System.out.println("count"+count); } private static void update(){ count.incrementAndGet(); }}Copy the code

Difference between LongAddr and AtomicLong

The principle of AtomicLong is to rely on the underlying CAS to ensure atomicity of the updated data. When adding or decreasing, it will use an infinite loop to keep cas to a specific value, so as to achieve the purpose of updating data. If the competition is not intense, the modification has a high chance of success, otherwise it has a high chance of failure, and at a high chance of failure, these atomic operations will be repeated over and over again, and performance will suffer.

For plain Long and Doubble variables, the JVM allows a 64-bit read or write operation to be split into two 32-bit operations.

LongAdder maintains an array of Cells internally, and each Cell has a long variable with an initial value of 0. In the case of the same amount of concurrency, the number of threads competing for a single variable will be reduced, which is a way to reduce the number of concurrent contention for shared resources. When multiple threads compete for the same atomic variable, If the failure is not a spin CAS retry, but an attempt to acquire the lock of another atomic variable, the current value will be returned by adding the values of all variables plus the value of base when obtaining the current value.

The core of LongAdder is to separate the hot data. For example, it can separate the core data value inside AtomicLong into an array. When each thread accesses it, it can use algorithms such as hash to map to one of the numbers for counting, and the final counting result will sum up the data. The hotspot data value is separated into multiple cells, and each cell maintains its internal value independently. The actual value of the current object is accumulated by all cells. In this way, hotspot data is effectively separated and parallelism is improved.

On the basis of AtomicLong, LongAdder distributes the update pressure of single point to each node. In low concurrency, the performance of LongAdder can be guaranteed to be basically consistent with AtomicLong by directly updating base, while in high concurrency, the performance can be improved by dispersion. The disadvantage is that if the LongAdder has concurrent updates during the statistics, the statistical data may have errors.

Use scenarios of LongAddr and AtomicLong

In practice, it is possible to use LongAdder rather than AtomicLong when dealing with high concurrency, but it is easier, more practical and more efficient to use AtomicLong when thread contention is low. In other cases, such as serial number generation, where accurate values are needed, globally unique AtomicLong is the right choice, not LongAdder