Java Multithreading (four)--atomic of thread concurrency library

Source: Internet
Author: User
Tags cas volatile wrapper

First, start with the atomic operation

Start with a relatively simple atomic (Java.util.concurrent is a queue-based contract and the queue, in many cases, uses the atomic operation, so begin here first).

In many cases we just need a simple, efficient, thread-safe increment-decrement scheme. Note that there are three conditions:

    1. Simple, means that the programmer as little as possible to operate the underlying or to achieve a relatively easy;
    2. High efficiency means less consumption of resources and faster process processing;
    3. Thread safety is also very important, which can guarantee the correctness of the data in multi-threading.

These three conditions look relatively simple, but they are difficult to achieve satisfactorily.

Typically, in Java, ++i or-I are not thread-safe, there are three separate operations: either the current value of the variable, the value +1/-1, and the new value is written back. In the case where no additional resources are available, only the lock can be used to guarantee the read-change-write "atomicity" of the three operations.

Everything starts with the Java.util.concurrent.atomic.AtomicInteger.

int addandget (int delta)
Adds the given value in atomic form to the current value. is actually an I-=i+delta operation that is equal to the thread-safe version.

Boolean compareandset (int expect, int update)
If the current value = = expected value, the value is atomically set to the given update value. Returns true if successful, otherwise false, and does not modify the original value.

int Decrementandget ()
atomically subtract the current value by 1. Equivalent to the thread-safe version of the-I operation.

int get ()
Gets the current value.

int getandadd (int delta)
Adds the given value in atomic form to the current value. Equivalent to a thread-safe version of T=i;i+=delta;return T;

int Getanddecrement ()
atomically subtract the current value by 1. Equivalent to a thread-safe version of the i--operation.

int Getandincrement ()
Atomically adds 1 to the current value. Equivalent to a thread-safe version of the i++ operation.

int getandset (int newvalue)
Atomically sets the given value and returns the old value. Equivalent to a thread-safe version of T=i;i=newvalue;return T;

int Incrementandget ()
Atomically adds 1 to the current value. Equivalent to a thread-safe version of the ++i operation.

void Lazyset (int newvalue)
Finally set to the given value. The delay sets the value of the variable, which is equivalent to the set () method, but because the field is volatile, the modification of the field will have a slight performance delay (albeit negligible) than the normal field (non-volatile field), so if you do not want to read the new value of the setting immediately, allow the "background" This method is useful if you modify the value. If it is still difficult to understand, it is similar to starting a background thread, such as performing a task to modify the new value, the original thread does not wait for the result of the modification to return immediately (this explanation is actually incorrect, but it is understood).

void set (int newvalue)
Set to the given value. Modifies the original value directly, that is, the i=newvalue operation.

Boolean weakcompareandset (int expect, int update)
If the current value = = expected value, the setting is atomically set to the given update value. The JSR specification reads and conditionally writes variables but does not create any happen-before sort, and therefore does not provide a weakcompareandset Any guarantees related to previous or subsequent read or write operations of any variables outside the target. The main idea is that when calling Weakcompareandset, there is no guarantee that no happen-before will occur (that is, there may be a command reordering that causes this operation to fail). However, from the Java source, in fact, this method does not implement the requirements of the JSR specification, the final effect and compareandset are equivalent, all call the Unsafe.compareandswapint () to complete the operation.

PackageXylz.study.concurrency.atomic;importJava.util.concurrent.atomic.atomicinteger;importOrg.junit.test;import Static org.junit.assert.*;p Ublic Classatomicintegertest {@Test public void Testall () throwsinterruptedexception{Final Atomicinteger value = new Atomicinteger (ten); Assertequals (Value.compareandset (1, 2 ), false); Assertequals (Value.get (),asserttrue (Value.compareandset (3)), Assertequals (Value.get (), 3); Value.set (0);//Assertequals (Value.incrementandget (), 1); Assertequals (Value.getandadd (2), 1); Assertequals (Value.getandset (5), 3); Assertequals (Value.get (), 5);//FINAL int threadsize = ten; thread[] ts = new thread[threadsize]; for (int i = 0; i < threadsize; i++) {ts[i] = new Thread () {Publ IC void Run () {value.incrementandget ();}}; }//for (thread t:ts) {T.start ();}for (thread t:ts) {t.join ();}//Assertequals (Value.get (), 5+Threadsi Ze); }}

Atomicinteger and Atomiclong, Atomicboolean, Atomicreference almost, here is not introduced. In the next article, we introduce atomic operations for arrays, fields, and other aspects.

Two, an array, a referenced atomic operation

In this section, we begin to discuss array atomic operations and some other atomic operations.

Atomicintegerarray

Atomiclongarray

Atomicreferencearray 's API is similar, select a representative atomicintegerarray to describe these issues.

int get (int i)

Gets i The current value of the position. Obviously, because this is an array operation, there is an index cross-boundary problem (Indexoutofboundsexception exception).

For the following API starting and Atomicinteger is similar, this method, the name of the parameter can get the function of the meaning of the writing is very commendable.

void set (int i, int newvalue)
void Lazyset (int i, int newvalue)
int getandset (int i, int newvalue)
Boolean compareandset (int i, int expect, int update)
Boolean weakcompareandset (int i, int expect, int update)
int getandincrement (int i)
int getanddecrement (int i)
int getandadd (int i, int delta)
int incrementandget (int i)
int decrementandget (int i)
int addandget (int i, int delta)

As a whole, the atomic operations of arrays are relatively easy to understand, and these APIs are more useful to realize their benefits than just stay in the theoretical stage.

Now focus on the atomic update of the field.

atomicintegerfieldupdater<t>/atomiclongfieldupdater<t>/ Atomicreferencefieldupdater<T,V> is the value of the field based on the reflected atom update.

The corresponding API is also very simple, but there are some constraints.

(1) The field must be of type volatile! In a later chapter, I'll explain in detail why it must be a volatile,volatile.

(2) The description type (modifier public/protected/default/private) of the field is consistent with the caller's relationship to the Action Object field. That is, the caller is able to manipulate the object field directly, then the atomic operation can be reflected. However, for a field of the parent class, the subclass cannot be manipulated directly, although the subclass can access the fields of the parent class.

(3) can only be an instance variable, not a class variable, that is, the static keyword cannot be added.

(4) can only be modifiable variables, cannot make final variables, because final semantics is not modifiable. There is actually a conflict between final semantics and volatile, and these two keywords cannot exist at the same time.

(5) for atomicintegerfieldupdater and atomiclongfieldupdater only fields of type Int/long can be modified, their wrapper type cannot be modified (integer/ Long). If you want to modify the wrapper type, you need to use atomicreferencefieldupdater.

The method of operation is described in the following example.

Packagexylz.study.concurrency.atomic; ImportJava.util.concurrent.atomic.AtomicIntegerFieldUpdater; public classAtomicintegerfieldupdaterdemo {class demodata{public volatile int value1 = 1 ; volatile int value2 = 2 ; protected volatile int value3 = 3 ; private volatile int value4 = 4 ;} Atomicintegerfieldupdater<demodata>  getupdater (String fieldName) {return Atomicintegerfieldupdater.newupdater (Demodata.class , fieldName),} void  doit () {demodata data = new  Demodata (); System.out.println ("1 ==>" +getupdater ("value1"). Getandset (data, )); System.out.println ("3 ==>" +getupdater ("value2" ). Incrementandget (data)); System.out.println ("2 ==>" +getupdater ("Value3" ). Decrementandget (data)); System.out.println ("True ==>" +getupdater ("Value4"). Compareandset (Data, 4, 5 ));} public static void  main (string[] args) {Atomicintegerfieldupdaterdemo demo = new  Atomicintegerfieldupdaterdemo (); Demo.doit (); }} 

In the example above, demodata field Value3? /value4 is not visible to the Atomicintegerfieldupdaterdemo class, so it is not possible to modify its value directly by reflection.

The atomicmarkablereference class describes an <Object,Boolean> pair that can be atomically modified by an Object or Boolean value, which is useful in some caches or state descriptions. This structure can effectively improve throughput when a single or simultaneous modification of the Object/boolean is possible.

The atomicstampedreference class maintains an object reference with an integer "flag" that can be updated atomically. Comparing the <object,boolean> of the atomicmarkablereference class,atomicstampedreference maintains a similar <object, The data structure of int> is actually a concurrent count of objects (references). However, unlike Atomicinteger , this data structure can carry an object reference, and the ability to atomically manipulate both the object and the count.

The "ABA Problem" is mentioned in later chapters, and atomicmarkablereference/atomicstampedreference is useful in solving "ABA problems" .

The use of atomic operations is probably so much, generally speaking is relatively clear, in the next chapter, the atomic operation of the object is summarized, focusing on the principle of atomic operation and design ideas.

Third, order reordering and Happens-before law

In this summary, we focus on the principle and design idea of atomic operation.

Because of the locking mechanism in the next section, the concept of locking is introduced appropriately in this section.

In the Java Concurrency in practice, this defines thread-safe :

This class is thread-safe when multiple threads access a class, without regard to the scheduling and alternation of these threads in the run-time environment, and without the need for additional synchronization and other coordination in the caller's code, which is still the correct behavior.

a stateless object is always thread-safe , obviously only when resources are competing to cause thread insecurity.

An atomic operation is a description of an operation that occurs when multiple threads are performing an action, either of which is completely complete or does not have any steps to perform the operation, which is atomic.

? atomic operations are not necessarily thread-safe: Because each thread operates a copy of the variable for the worker thread of its own thread.

The boring definition is finished, the following is more boring theoretical knowledge.

Order reordering

The Java language specification specifies that sequential semantics are maintained within the JVM thread, meaning that the order of execution of the instructions may not be consistent with the order of the Code as long as the final result of the program equals the result of its strict sequential environment. This process is done by reordering called directives. The meaning of command reordering is that the JVM is able to properly reorder machine instructions based on the characteristics of the processor (multi-level cache system, multi-core processor, etc.), so that the machine instruction is more consistent with the performance of the CPU, and the machine is maximized.

The simplest model of the program executes in the order in which the instructions appear, which is independent of the CPU of the executing instruction, maximizing the portability of the instructions. The technical terminology of this model is called sequential conformance model. However, both modern computer systems and processor architectures do not guarantee this (because human designations are not always guaranteed to conform to the CPU processing characteristics).

Happens-before Law

The Java storage model has a happens-before principle that if action B is to see the result of action a (whether or not A/b is executed within the same thread), then A/b needs to satisfy the happens-before relationship.

Introduce a concept before introducing the Happens-before rule: jmm action (Java Memeory Model action), Java Storage Model action. An action includes: Read and write variables, monitor lock and release locks, start () of a thread, and join (). The lock is also mentioned later.

Happens-before Complete rule:

(1) Each action in the same thread is happens-before to any action that appears behind it.

(2) The unlocking of a monitor happens-before to each subsequent locking of the same monitor.

(3) The write operation of the volatile field happens-before the read operation for each subsequent same field.

(4) The Thread.Start () call will happens-before the action in the startup thread.

(5) All actions in thread are happens-before to other threads to check that the thread ends or thread.join () returns or thread.isalive () ==false.

(6) One thread A calls another another thread B's interrupt () is happens-before on thread A to find B is interrupted by a (b throws an exception or a detects B's isinterrupted () or interrupted ()).

(7) The end of an object constructor Happens-before with the beginning of the finalizer of the object

(8) If a action Happens-before B action, and B action Happens-before and C action, then a action Happens-before C action.

volatile Semantics

So far, we've talked about volatile many times, but we still don't understand the semantics of volatile.

Volatile is equivalent to weak implementations of synchronized, which means that volatile implements a semantics similar to synchronized without locking mechanisms. It ensures that updates to volatile fields inform other threads in a predictable manner.

Volatile contains the following semantics:

(1) The Java storage model does not reorder the operations of the valatile instruction: This ensures that the operation of the volatile variable is performed in the order in which the instructions appear.

(2) Volatile variables are not cached in the register (only if the owning thread is visible) or other places where the CPU is not visible, and the result of the volatile variable is always read from main memory each time. In other words, for changes to volatile variables, other threads are always visible and are not using variables inside their own thread stacks. That is, in the Happens-before rule, after a write operation to a valatile variable, any subsequent read operation understands the result of this write operation.

Although the volatile variable is good, volatile is not guaranteed to be thread-safe, which means that the operation of the volatile field is not atomic, and the volatile variable can only guarantee visibility (after a thread has been modified, other threads can understand the result of seeing this change). To ensure atomicity, so far only lock!

Volatile is usually in the following scenario:

Volatile Boolean done = False;    .. while (. Done ) {        dosomething ();    } 

Three principles for applying volatile variables:

(1) The write variable does not depend on the value of this variable, or only one thread modifies the variable

(2) The state of the variable does not need to participate in the invariant constraint with other variables

(3) Access variables do not need to be locked

Four CAS Operations

Prior to JDK 5, the Java language was guaranteed to be synchronized by the Synchronized keyword, which would result in a lock (later chapters will also talk about locks).

The lock mechanism has the following issues:

(1) Under the multi-thread competition, the locking and release lock will lead to more context switching and scheduling delay, causing performance problems.

(2) A thread holding a lock causes all other threads that require this lock to hang.

(3) If a high-priority thread waits for a thread with a lower priority to release the lock, it causes the priority to be inverted, causing a performance risk.

Volatile is a good mechanism, but volatile does not guarantee atomicity. Therefore, for synchronization to eventually return to the lock mechanism.

An exclusive lock is a pessimistic lock, and synchronized is an exclusive lock that causes all other threads that need to be locked to hang, waiting for the thread that holds the lock to release the lock. Another more effective lock is the optimistic lock. The so-called optimistic lock is that each time without locking but assuming that there is no conflict to complete an operation, if the conflict failed to retry until successful.

CAS Operations

The mechanism used for optimistic locking above is cas,compare and Swap.

CAS has 3 operands, a memory value of V, an old expected value of a, and a new value to be modified B. If and only if the expected value A and the memory value of the V phase, the memory value of V is modified to B, otherwise do nothing.

Non-blocking algorithm (nonblocking algorithms)

Failure or suspend of one thread should not affect other threads ' failed or suspended algorithms.

Modern CPUs provide special instructions to automatically update shared data and detect interference from other threads, and Compareandset () replaces the lock with these.

Take out Atomicinteger to study how the data is correct without a lock.

private volatile int value;

First of all, in the absence of a lock mechanism may require the use of volatile primitives, to ensure that data between threads is visible (shared).

This allows the value of the variable to be read directly when it is obtained.

Public final int get () {
return value;
}

Then let's see how ++i did it.

Public final int Incrementandget () {
for (;;) {
int current = get ();
int next = current + 1;
if (Compareandset (current, next))
return next;
}
}

The CAS operation is used here, each time the data is read from memory and then the CAS operation is performed with the result of +1, if successful, the result is returned, otherwise it is retried until it succeeds.

Instead, Compareandset uses JNI to perform the operation of the CPU instructions.

Public final Boolean compareandset (int expect, int update) {
Return Unsafe.compareandswapint (this, valueoffset, expect, update);
}

The whole process is like this, using the CPU's CAS instruction, and using JNI to complete the Java non-blocking algorithm. Other atomic operations are done using a similar feature.

And the whole j.u.c is built on the CAs, so for synchronized blocking algorithm, J.U.C has a great improvement in performance. The resources article describes the use of CAs to build data structures such as non-blocking counters, queues, and so on.

CAS looks cool, but it can cause "ABA problems".

The CAS algorithm implements an important premise that needs to take out the data at some point in memory, and compare and replace at the next moment, then the time difference class will cause the data to change.

For example, a thread one takes a from the memory location V, when another thread two also takes a from memory, and two does something to B, and two then turns the data in the V position into a, when the CAS operation finds that the memory is still a, and then the one operation succeeds. Although the CAS operation of thread one is successful, it does not mean that the process is not a problem. If the head of the list is changed two times, the original value is restored, but the list does not change. So the atomic operation Atomicstampedreference/atomicmarkablereference mentioned earlier is very useful. This allows a pair of changed elements to operate atomically.

Java Multithreading (four)--atomic of thread concurrency library

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.