directory

  • define
  • atomic
    • AtomicXxx
    • AtomicStampedReference
  • visibility
  • order
  • The resources

Definition First you need to think about what is thread safety?

The concurrent Java programming practical definition is given in the book: when multiple threads access a class, regardless of the runtime environment using the scheduling or how will these threads execute alternately, and in the calling code does not require any additional synchronization, this class can show the correct behavior, then this class is thread-safe.

Thread safety mainly starts from the following aspects: atomicity, orderliness and visibility.

Atomicity: Provides mutually exclusive access, and only one thread can operate on data at a time. For example: atomicXXX class, synchronized keyword application.

Orderliness: a thread observes the order in which instructions are executed in other threads. Due to instruction reordering, this observation is generally disordered. For example, the happens-before principle.

Visibility: Changes made to main memory by one thread can be seen by other threads in a timely manner; Examples are synchronized,volatile.

atomic

 AtomicXxx 

Talking about Atomic must leave the well-known Atomic package, inside the JDK offers many Atomic classes, AtomicInteger, AtomicLong, AtomicBoolean and so on.

Take AtomicInteger as an example:

class AtomicIntegerExample { private static final Logger log = LoggerFactory.getLogger(AtomicIntegerExample.class); Public static int requestTotal = 500; Public static int threadTotal = 20; public static AtomicInteger count = new AtomicInteger(0); public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newCachedThreadPool(); Final Semaphore Semaphore = new Semaphore(threadTotal); Final CountDownLatch CountDownLatch = new CountDownLatch(requestTotal); for (int i = 0; i < requestTotal ; i++) { executorService.execute(() -> { try { semaphore.acquire(); add(); semaphore.release(); } catch (Exception e) { log.error("exception", e); } countDownLatch.countDown(); }); } countDownLatch.await(); executorService.shutdown(); log.info("count:{}", count.get()); } private static void add() { count.incrementAndGet(); }}Copy the code

Follow the Demo and try debuge to see how the low-level implementation works. Key method: incrementAndGet ()

	/**
     * Atomically increments by one the current value.
     *
     * @return the updated value
     */
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
Copy the code

An implementation of optimistic locking is the incrementAndGet method in AtomicInteger, which updates values in memory using spin (cyclic detection of updates) and ensures that the update operation is atomic by executing the underlying CPU.

What is the problem with using a spinlock mechanism??

If the spin is unsuccessful for a long period of time, it can be very expensive for the CPU to execute.

Looking UnSafe, the first method to address broadening concerns is getAndAddInt, the magic class UnSafe. Looking UnSafe, we’ll soon have an article on this topic, too

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

Take a look at the code structure of this method: do-while(), and then understand the execution logic.

Get the current value by calling getIntVolatile(), using the object’s reference to the offset of the value, and then calling compareAndSwapInt to check that if the value in obj is equal to Expect, no other thread has changed the variable, and update it to update. If the CAS does not succeed at this step, continue with the CAS operation using spin.

The above method parameters need a special explanation, otherwise it will be really confusing:

The desired goal of compareAndSwapInt() is for the var1 object, if the current value var2 is equal to the underlying value var5, then update it to the later value (var5+var4).

Hope we can understand clearly, more importantly, xiaobian do not understand the mistake, if there is a problem, I hope big private improper, timely correction.

The core idea of atomic low-level implementation is CAS, but THERE is ABA problem in CAS.

CompareAndSet first checks whether the current reference is equal to the expected reference, and whether the current flag is equal to the expected flag, and if all are equal, sets the reference and the flag’s value atomically to the given updated value.

What is ABA??

If A value is A, becomes B, and becomes A again, then checking with CAS will find that its value has not changed, but in fact it has. This is the ABA problem with CAS.

That face ABA problem, everybody is thinking how to solve?? Think about optimistic locking in a database, version number. JDK introduces AtomicStampedReference…

AtomicStampedReference 

Take a look at the methods of this class and pay attention to the translation comments to understand the meaning of each parameter

/**
     * Atomically sets the value of both the reference and stamp
     * to the given update values if the
     * current reference is {@code ==} to the expected reference
     * and the current stamp is equal to the expected stamp.
     *
     * @param expectedReference the expected value of the reference
     * @param newReference the new value for the reference
     * @param expectedStamp the expected value of the stamp
     * @param newStamp the new value for the stamp
     * @return {@code true} if successful
     */
    public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
        Pair<V> current = pair;
        return
            expectedReference == current.reference &&
            expectedStamp == current.stamp &&
            ((newReference == current.reference &&
              newStamp == current.stamp) ||
             casPair(current, Pair.of(newReference, newStamp)));
    }
Copy the code

This method checks whether the current reference is equal to the expected reference and whether the current flag is equal to the expected flag;

If all are equal, the values of the reference and flag are set atomically to the given updated value.

visibility

Quick highlights: What is visibility between threads?

Changes made by one thread to the value of a shared variable can be seen by other threads in real time.

What are shared variables?

If a variable has copies in the working memory of multiple threads, it is a variable shared by all threads.

What is the Java Memory model?

Java Memory Model (JMM) The JMM describes the rules for accessing various variables (shared by threads) in a Java program, as well as the low-level details of storing variables into and reading variables out of Memory within the JVM.

Rule 1:

1> All variables are stored in main memory

2> Each thread has its own independent working memory, which holds a copy of the variable used by the thread (a copy of the variable in main memory).

Rule 2:

1> All operations on shared variables must be performed by the thread in its own working memory and cannot be read or written directly from main memory

2> Different threads cannot directly access variables in the working memory of other threads. The transfer of variables between threads needs to be completed through the main memory.

order

Orderliness means that when a program is executed, its code is executed in the same order as its statements.

Why are there inconsistencies? – reorder

In the Java memory model, the compiler and processor are allowed to reorder instructions, but the reordering process does not affect the execution of a single-threaded program, but affects the correctness of multithreaded concurrent execution.

The happends-before principle is the happends-before principle. The happends-before principle is the happends-before principle

1. Program order rule: in a thread, according to the code order, the first operation written before the operation written later; 2. Lock rule: An unLock operation occurs first when the same lock operation is performed later. 3. The volatile variable rule: Writes to a variable occur before reads to that variable; 4. Transfer rule: If operation A precedes operation B and operation B precedes operation C, it can be concluded that operation A precedes operation C; 5. Thread start rule: The start() method of the Thread object occurs first for each action of the Thread; 6. Thread interrupt rule: Calls to the threadinterrupt () method occur first when the code of the interrupted thread detects the occurrence of an interrupt event; 7. Thread termination rule: All operations in a Thread occur before Thread termination detection. We can detect that Thread has terminated by means of the end of thread.join () method and the return value of thread.isalive (). 8. Object finalization rule: The finalization of an object happens first at the beginning of its Finalize () method;Copy the code

For thread visibility and order of understanding, need to build Java memory model on the basis of understanding and thinking, although understand a little abstract, each time to read a series of articles, are able to harvest different knowledge points, the book read hundreds of times its meaning from see, ha ha,, continue to come on!! Hats off to every programmer who is working hard!!

Reference: blog.csdn.net/xuan\_lu/ar… ?

Four things to watch ❤️

If you find this article helpful, I’d like to invite you to do three small favors for me:

  1. Like, forward, have your “like and comment”, is the motivation of my creation.

  2. Follow the public account “JavaAC” to share original knowledge from time to time.

  3. Also look forward to the follow-up article ing🚀

  4. Pay attention to the scan code to obtain the learning materials package