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:
- Fields that are not volatile cannot be updated atomically (thread visible after volatile modification)
- Class variables cannot be updated atomically (that is, fields cannot be modified static)
- Member variable properties that are not directly accessible cannot be updated atomically (that is, fields cannot be private decorated)
- Fields modified by final cannot be updated atomically
- 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!