JUC - Atomic Class Operation (3)

Atomic class operations

  Since concurrent access is emphasized, the operating system bitness must be considered: 32-bit operating system or 64-bit operating system, and for long data types, it is 64-bit. But if the project runs on a 32-bit system now, the long data will occupy 32-bit space for data storage.

If the long type is used in every program class now, then the volatile configuration needs to be manually configured when processing, which is too troublesome.

In order to solve this problem, an atomic subpackage is provided in juc, which stores atomic operation data, that is to say, the data types contained in it are declared with volatile.

 

Atomic Operation Classification

 Atomic operation: It means that the operation process will not be interrupted, and the data operation is guaranteed to be carried out in an atomic manner.

  • Basic data types: AtomicInteger, AtomicLong, AtomicBoolean
  • Array type: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray
  • Reference types: AtomicReference, AtomicStampedRerence, AtomicMarkableReference
  • Object's property modification type: AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater.

Example: Observing the "AtomicLong" type

  In fact, when using "AtomicLong", it contains "private volatile long value".

package so.strong.concurrents;
import java.util.concurrent.atomic.AtomicLong;
public class StrongDemo {
    public static void main(String[] args) throws Exception {
        AtomicLong num = new AtomicLong(10);
        System.out.println( "Data increment: "+ num.incrementAndGet());
        System.out.println( "Data decrement: "+ num.decrementAndGet());
    }
}

  If you want to perform basic mathematical operations on data types, you also need to use methods.

 

Example: Implementing Basic Mathematical Operations

package so.strong.concurrents;
import java.util.concurrent.atomic.AtomicLong;
public class StrongDemo {
    public static void main(String[] args) throws Exception {
        AtomicLong num = new AtomicLong(100);
        System.out.println( "Addition operation: "+num.addAndGet(10 ));
        System.out.println( "Subtraction operation: "+num.addAndGet(-9 ));
    }
}

  After all, this operation is not a primitive basic data type, and its operation needs to ensure the concurrency safety of data under multi-threaded access.

For atomic processing, the above is not its focus, but its operation form. There is one of the most important methods, the CAS method:

public final boolean compareAndSet(long expect, long update) {
      return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}

 

Example: Observe the use of the CAS method

package so.strong.concurrents;
import java.util.concurrent.atomic.AtomicLong;
public class StrongDemo {
    public static void main(String[] args) throws Exception {
        AtomicLong num = new AtomicLong(100); // Set the atomic operation
         // If the content to be modified now is 100, that is: the original atomic type is 100, then use 300 to replace the content of num 
        System.out.println( num.compareAndSet(101,300));   // The comparison value is equal to 100, return true 
        System.out.println(num);
    }
}

When using the CAS method to modify the content, it is necessary to set an original comparison content. If the content is the same, it can be modified. If the current operation is an array, there is also a corresponding program class AtomicLongArray.

AtomicLongArray has two constructors:

  • Dynamic development
    public AtomicLongArray(int length) {
         array = new long[length];  
    }
  • static development
    public AtomicLongArray(long[] array) {
        this.array = array.clone();
    }

     

Example: Performing Array Operations

package so.strong.concurrents;
import java.util.concurrent.atomic.AtomicLongArray;
public class StrongDemo {
    public static void main(String[] args) throws Exception {
        AtomicLongArray array = new AtomicLongArray(new long[]{1,2,3});
        array.set( 0,99); // Atomic arrays must use set to modify the content 
        System.out.println(array);
    }
}

  In addition to the atomic processing support for long types, atomic operations can also be performed on reference types (objects).

Example: Using Atomicity to Describe Objects

package so.strong.concurrents;
import java.util.concurrent.atomic.AtomicReference;
public class StrongDemo {
    public static void main(String[] args) throws Exception {
        AtomicReference<Member> ref = new AtomicReference<>();
        Member memA = new Member("张三", 22);
        Member memB = new Member("李四", 32);
        ref.set(memA);
        ref.compareAndSet(memA, memB); // Object reference changes can only rely on address comparison "==" 
        System.out.println(ref);
    }
}

class Member {
    private String name;
    private int age;
    public Member(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "name= " + this.name + ",age= " + this.age;
    }
}

 

Strictly speaking, the above types are all commonly used processing forms, but in Java development, you may encounter a very strange problem, that is: the type defined in the class itself may not be AtomicLong, then you can use the AtomicLongFieldUpdater class To be done.

public  abstract  class   AtomicLongFieldUpdater<T> 
  // Abstract class AtomicLongFieldUpdater updater
The method of the AtomicLongFieldUpdater updater to get the object:
@CallerSensitive
public static <U> AtomicLongFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) {
    Class<?> caller = Reflection.getCallerClass();
     if (AtomicLong.VM_SUPPORTS_LONG_CAS)
        return new CASUpdater<U>(tclass, fieldName, caller);
     else
        return new LockedUpdater<U>(tclass, fieldName, caller);
}

 

Example: Using the AtomicLongFieldUpdater Updater

package so.strong.concurrents;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
public class StrongDemo {
    public static void main(String[] args) throws Exception {
        Book book = new Book(100001, "Java from entry to abandonment" );
        book.setBid(200002); //修改bid
        System.out.println(book);
    }
}

class Book {
    private volatile long bid; //必须追加volatile关键字
    private String title;
    public Book(long bid, String title) {
        this.bid = bid;
        this.title = title;
    }

    @SuppressWarnings({"rawtypes","unchecked"})
    public void setBid(long bid) {
        AtomicLongFieldUpdater updater = AtomicLongFieldUpdater.newUpdater(super.getClass(), "bid");
        updater.compareAndSet( this , this .bid, bid); // Use the CAS method to modify the content 
    }

    @Override
    public String toString() {
         return "Book number: " + this .bid + ", name: " + this .title;
    }
}

 

In order to ensure the integrity of multi-bit data types in concurrent access, the volatile keyword must be used . At the same time, a large number of atomic operation classes will be found in the entire juc package.

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324808045&siteId=291194637