Introduction to the

Today we’ll look at a few more atomic classes that might be used during development, or that you might be asked about in an interview.

Respectively is:

1, the AtomicReference

2, AtomicStampedReference

3, AtomicArray

4, AtomicFieldUpdater

The classes we studied in the previous two sections were designed for atomic reads and writes of the base type. These are all designed for atomic operations that reference types. How do we use them? What about usage scenarios?


AtomicReference

This class provides non-blocking atomic read and write operations for object references

That old rules, we still have a look at its source:

public class AtomicReference<V> implements java.io.Serializable {
    private static final long serialVersionUID = -1848883965231344442L;

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;
	// Volatile modifies a generic value attribute
    private volatile V value;
}
Copy the code

As described in the introduction: atomic operations that provide object references; It’s basically the same API as AtomicInteger, so I’m not going to go through all of them.

Let’s briefly simulate a raffle:

You all own a large company, and you raffle cars on holidays (rich people are so willful); Let’s say there are 100 cars, and there are a lot of people in the lottery, and even though everyone is rich and capricious, there are only so many cars to buy, so you have to control the number of winners precisely.

Without further ado, let’s go straight to the code (real system sweepstakes are definitely better) :

// Define our prize category
@Data
public class Prize {

    /** * First Prize: Xiao Mi Automobile */
    private String level;

    /** * quantity */
    private int count;

    public Prize(String level, int count) {
        this.level = level;
        this.count = count; }}Copy the code

Let’s start with a non-thread-safe code test:

public static void main(String[] args) {
        Prize prize = new Prize("Xiaomi Car".100);
        AtomicInteger atomicInteger = new AtomicInteger();
        IntStream.range(0.300).forEach(
                value -> {
                    new Thread(
                            () -> {
                                // get the current number
                                int count = prize.getCount();
                                if (count > 0) {
                                    //② Subtract 1 from the remaining number sources and update them back to the prize pool
                                    prize.setCount(count - 1);
                                    atomicInteger.incrementAndGet();
                                    log.info("Current thread :{}, grab {}", Thread.currentThread().getName(), count); } } ).start(); });try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("Number of winners: {}", atomicInteger.get());
    }
Copy the code

The above code has thread unsafe. The essence of its insecurity is: when thread 1 and thread 2 execute to ① at the same time, if they both get the current remaining number 10, continue to execute to ②, they both subtract 1, and finally the two threads update back to 9. There are many solutions to this situation, but I chose to use the AtomicReference class for testing:

public static void main(String[] args) {
  		// Encapsulate our initial prize pool into an AtomicReference
        AtomicReference<Prize> reference = new AtomicReference<>(new Prize("Xiaomi Car".100));
        AtomicInteger atomicInteger = new AtomicInteger(0);
        IntStream.range(0.300).forEach(
                value -> {
                    new Thread(
                            () -> {
                                // get the current number of objects
                                final Prize prize = reference.get();
                                if (prize.getCount() > 0) {
                                  	// subtract 1 from the remaining number sources
                                    Prize prizeNew = new Prize(prize.getLevel(), reference.get().getCount() - 1);
                                  	// update the data to the prize pool
                                    if (reference.compareAndSet(prize, prizeNew)) {
                                        log.info("Current thread :{}, grab {}", Thread.currentThread().getName(), prize.getCount()); atomicInteger.incrementAndGet(); } } } ).start(); });try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("Number of winners: {}", atomicInteger.get());
    }
Copy the code

Look at the code above: The **reference.compareAndSet(Prize, prizeNew)** update fails if the reference to the prize object has been modified by another thread. For this thread, good anger ah, hand into the lottery box, or did not grab the prize; It doesn’t matter to the boss, just send the specified amount; For this purpose, a while loop allows the thread to retry (touch once, how many more times?).

Are you familiar with the use of AtomicReference?

AtomicReference, display Lock, synchronized performance:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Fork(1)
@Warmup(iterations = 5)
@Measurement(iterations = 10)
public class SynchronizedVsAtomicReference {

    @State(Scope.Group)
    public static class MonitorSync {
        private final Prize prize = new Prize("Xiaomi Car".100);

        public void syncDeduct(a) {
            synchronized (SynchronizedVsAtomicReference.class) {
                int count = prize.getCount();
                if (count > 0) {
                    prize.setCount(count - 1); }}}}@State(Scope.Group)
    public static class MonitorReference {

        private final AtomicReference<Prize> reference = new AtomicReference<>(new Prize("Xiaomi Car".100));

        public void referenceDeduct(a) {
            final Prize p = reference.get();
            final Prize newP = new Prize(p.getLevel(), p.getCount() - 1); reference.compareAndSet(p, newP); }}@State(Scope.Group)
    public static class MonitorLock {
        private final Prize prize = new Prize("Xiaomi Car".100);
        private final Lock lock = new ReentrantLock();

        public void lockDeduct(a) {
            try {
                lock.lock();
                int count = prize.getCount();
                if (count > 0) {
                    prize.setCount(count - 1); }}finally{ lock.unlock(); }}}@GroupThreads(10)
    @Group("sync")
    @Benchmark
    public void syncDeduct(MonitorSync monitorSync) {
        monitorSync.syncDeduct();
    }

    @GroupThreads(10)
    @Group("reference")
    @Benchmark
    public void referenceDeduct(MonitorReference monitorReference) {
        monitorReference.referenceDeduct();
    }

    @GroupThreads(10)
    @Group("lock")
    @Benchmark
    public void lockDeduct(MonitorLock monitorLock) {
        monitorLock.lockDeduct();
    }

    public static void main(String[] args) throws Exception {
        Options options = new OptionsBuilder().include(SynchronizedVsAtomicReference.class.getSimpleName())
                .addProfiler(StackProfiler.class)
                .build();
        newRunner(options).run(); }}Copy the code

The result is as follows:

Benchmark Mode Cnt Score Error Units SynchronizedVsAtomicReference. Lock avgt 10 0.251 + / - 0.020 us/op SynchronizedVsAtomicReference. Reference avgt 10. 0.328 + / - 0.022 us/op SynchronizedVsAtomicReference sync avgt 10 + / - 0.959 0.285 us/opCopy the code

The statistical results of thread status data are as follows:

SynchronizedVsAtomicReference. Lock: stack:78.1%         WAITING
 21.9% RUNNABLE SynchronizedVsAtomicReference. Reference: the stack:98.7%         RUNNABLE
  1.3% WAITING SynchronizedVsAtomicReference. Sync: the stack:79.8%         BLOCKED
 19.3%         RUNNABLE
  0.8%         WAITING
Copy the code

As can be seen from the above data, the performance result for my test code is:

Display Lock Lock > AtomicReference > synchronized

However, from the comparison of thread state statistics, the RUNNABLE state of AtomicReference thread is as high as 98.7%, while the RUNNABLE state of Lock thread is only 21.9%. Looking at the code, I should conclude that the object creation process in the referenceDeduct method increases the overall execution time; In my opinion, the performance of Atomic lock-free design is very good, so we should fully consider the current use scenarios when conducting thread safety processing, and choose the solution with excellent performance according to different scenarios.


AtomicStampedReference

So far we’ve looked at AtomicInteger, AtomicLong, AtomicBoolean, AtomicReference, They all adopt the operation mode of lockless based on volatile keyword +CAS algorithm to ensure the thread safety of shared data in multiple threads.

CAS algorithm we are certainly not unfamiliar, popular is the first comparison, and then update (exchange).

But suppose thread 1 changes A variable from A to B, and then from B to A; For thread 2, although the data is A, it does not know that the data has been modified (A is dirty, for the cleanliness of thread 1 can not tolerate), this is the CAS algorithm ABA problem, you have been asked by the interviewer in the interview?

The topic of this section, AtomicStampedReference, is an ABA problem; In the process of database operation, we also used the optimistic lock version number to solve the ABA problem. AtomicStampedReference also increases the version number:

Look at the constructor:

public AtomicStampedReference(V initialRef, int initialStamp) {
  pair = Pair.of(initialRef, initialStamp);
}
Copy the code

The initialStamp attribute in the constructor is a version number that needs to be maintained;

Note that this version number is the responsibility of the application itself, and the AtomicStampedReference does not provide security operations.

The API is relatively simple, so you can try it out.


AtomicArray

This class provides atomic operations on array data types: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray

Here’s a quick test:

@Test
public void addTest(a) {
  int[] intArray = {1.2.3.4.5.6};
  AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(intArray);

  // Add 10 to the element with index 2
  assert atomicIntegerArray.addAndGet(2.10) = =13;
  // Get the element with index 2
  assert atomicIntegerArray.get(2) = =13;
}
Copy the code

This class can be atomic for an operation on an index


AtomicFieldUpdater

This class provides operations for atomic updating of object attributes

public class AtomicFieldUpdaterTest {

    @Data
    public static class User {
        private String name;

        volatile int money;

        public User(a) {}public User(String name, int money) {
            this.name = name;
            this.money = money; }}@Test
    public void addTest(a) {
        AtomicIntegerFieldUpdater<User> updater = AtomicIntegerFieldUpdater.newUpdater(User.class, "money");
        User user = new User("Zhang".100);
        assert 120 == updater.addAndGet(user, 20); }}Copy the code

We have done a simple test, this AtomicIntegerFieldUpdater. NewUpdater (User. The class, “money”); You pass in the class” user. class” and the field “money”, and call updater’s API. All operations on the “money” field are atomic.

In particular, there is a lot to satisfy the attributes of atomic operations:

  1. Fields that are not volatile cannot be updated atomically (thread visible after volatile modification)
  2. Class variables cannot be updated atomically (that is, fields cannot be modified static)
  3. Member variable properties that are not directly accessible cannot be updated atomically (that is, fields cannot be private decorated)
  4. Fields modified by final cannot be updated atomically
  5. The member attributes of the parent class cannot be updated atomically

Our atomic type learning here, I believe you must have a further understanding of JAVA atomic types;

So our next system will learn JAVA concurrency utility classes, let’s take a step further into concurrent programming.

Portal of public Account — AtomicReference for Concurrent Programming

The code cloud code link is as follows:

Gitee.com/songyanzhi/…

Thanks for reading, and wish you all a happy and healthy work!