First, volatile is the keyword used in Java to modify variables, Atomicreference is the class under concurrent package java.util.concurrent.atomic.
First, the volatile effect, when a variable is defined as volatile, as a "lesser synchronized", with two features:
1. Ensure that this variable is visible to all threads (when a thread modifies the value of the variable, the new value is immediately known to other threads)
2. Prohibit order reordering
Note that volatile modifier variables are not guaranteed to be thread-safe under concurrency because the operations inside Java are not atomic.
Volatile description
The Java.util.concurrent.atomic toolkit, which supports thread-safe programming for unlocking on a single variable. The basic feature is that in a multithreaded environment, when there are multiple threads executing the methods contained by instances of these classes at the same time, there is exclusivity, that is, when a thread enters the method and executes the instruction, it is not interrupted by another thread, and the other thread is like a spin lock until the method executes. It is only a logical understanding that the JVM chooses a second thread from the waiting queue to enter.
Atomicreference description
Java Theory and Practice: using Volatile variables correctly
Volatile variables in the Java language can be thought of as "light", andsynchronizedsynchronizedvolatile variables require less coding and run-time overhead than blocks, but only part of the functionality that they can implementsynchronized. This article describes several patterns for using volatile variables effectively, and highlights several scenarios where volatile variables are not appropriate.
The lock provides two main features: Mutual exclusion (mutual exclusion) and visibility (visibility). Mutual exclusion allows only one thread to hold a particular lock at a time, so you can use this attribute to implement a coordinated access protocol to shared data so that only one thread can use that shared data at a time. Visibility is more complex, and it must ensure that the changes made to the shared data before the lock is released are visible to the other thread that subsequently acquired the lock-if there is no guarantee of this visibility provided by the synchronization mechanism, the shared variables that the thread sees may be pre-modified or inconsistent values, which can cause many serious problems.
Volatile variable
Volatile variables havesynchronizedthe visibility characteristics, but do not have atomic properties. This means that threads can automatically discover the latest values of volatile variables. Volatile variables can be used to provide thread safety, but can only be applied to a very limited set of use cases: there is no constraint between multiple variables or between the current value of a variable and the modified value. Therefore, using volatile alone is not sufficient to implement counters, mutexes, or any class that has invariant (invariants) associated with multiple variables (for example, "Start <=end").
For simplicity or scalability, you might prefer to use volatile variables instead of locks. Some idioms (idiom) are easier to encode and read when using volatile variables rather than locks. In addition, volatile variables do not cause threads to block like locks, and therefore are less likely to cause scalability problems. In some cases, if the read operation is much larger than the write operation, the volatile variable can also provide a performance advantage over the lock.
Conditions for correct use of volatile variables
You can use volatile variables instead of locks in a limited number of situations. For the volatile variable to provide ideal thread safety, the following two conditions must be met:
- The write operation on the variable does not depend on the current value.
- The variable is not contained in an invariant that has other variables.
In fact, these conditions indicate that these valid values that can be written to a volatile variable are independent of the state of any program, including the current state of the variable.
The first condition restricts the volatile variable from being used as a thread-safe counter. Although an incremental operation (x++) looks like a separate operation, it is actually a combination of a read-modify-write sequence of operations that must be performed atomically, and volatile does not provide the necessary atomic properties. Implementing the correct operation requires thatxthe values remain constant during the operation, and the volatile variable cannot achieve this. (However, if you adjust the value to write only from a single thread, the first condition can be ignored.) )
Most programming scenarios conflict with one of these two conditions, making volatile variables not assynchronizeduniversally applicable to thread safety. Listing 1 shows a non-thread-safe numeric range class. It contains an invariant-the nether is always less than or equal to the upper bound.
Listing 1. Non-thread-safe numeric range classes
@NotThreadSafe
public class NumberRange {
private int lower, upper;
public int getLower() { return lower; }
public int getUpper() { return upper; }
public void setLower(int value) {
if (value > upper)
throw new IllegalArgumentException(...);
lower = value;
}
public void setUpper(int value) {
if (value < lower)
throw new IllegalArgumentException(...);
upper = value;
}
}
This approach restricts the state variables of the scope, solowerdefining the and upper fields as volatile types does not fully implement the thread safety of the class, and thus still requires synchronization. Otherwise, if two threads are executed at the same time with inconsistent valuessetLowersetUpper, then the scope will be in an inconsistent state. For example, if the initial state is, at the(0, 5)same time, thread A is calledsetLower(4)and thread B is calledsetUpper(3), it is clear that the values crossed by these two operations are not eligible, then two threads will pass the check to protect the invariant so that the final range value is(4, 3)-an invalid value. As for other operations on the scope, we need to makesetLower()andsetUpper()operate atomically--and it is not possible to define a field as a volatile type.
Performance considerations
The main reason for using volatile variables is its simplicity: in some cases, it is much simpler to use volatile variables than to use the corresponding locks. The secondary cause of using volatile variables is its performance: in some cases, the performance of the volatile variable synchronization mechanism is better than the lock.
It is difficult to make accurate, comprehensive evaluations, such as "X is always faster than Y", especially for internal operations within the JVM. (for example, in some cases the VM might be able to completely remove the lock mechanism, which makes it difficult to comparevolatileandsynchronizedcost abstractly.) That is, the volatile read operation overhead is very low on most of the current processor architectures-almost the same as non-volatile read operations. The cost of volatile writes is much more than non-volatile writes, because to ensure that visibility requires memory Fence, even then, the total cost of volatile is still lower than lock fetching.
Volatile operations do not clog like locks, so volatile can provide some of the scalability characteristics that are better than locks in situations where volatile is safe to use. If the number of read operations is much greater than the write operation, volatile variables can often reduce the performance overhead of synchronization compared to locks.
Correct use of volatile patterns
Many concurrency experts in fact tend to steer users away from volatile variables, because using them is more error-prone than using locks. However, if you carefully follow some well-defined patterns, you can safely use volatile variables in many situations. Always keep in mind the limitations of using volatile--a rule that uses volatile--only if the state is truly independent of other content within the program avoids extending these patterns to unsafe use cases.
Mode #1: Status flag
Perhaps the canonical use of a volatile variable is to use only a Boolean status flag that indicates that an important one-time event has occurred, such as completion of initialization or request for downtime.
Many applications contain a control structure in the form of "do some work when you are not ready to stop the program," as shown in Listing 2:
Listing 2. Use the volatile variable as a status flag
volatile boolean shutdownRequested;
...
public void shutdown() { shutdownRequested = true; }
public void doWork() {
while (!shutdownRequested) {
// do stuff
}
}
It is likely that the method is called from outside the loop-shutdown()that is, in another thread-and therefore some synchronization is required to ensure thatshutdownRequestedthe visibility of the variable is implemented correctly. (may be called from a JMX listener, an action listener in the GUI event thread, through RMI, through a Web service, and so on). However,synchronizedwriting loops using blocks is much more cumbersome than using the volatile status flags shown in Listing 2. Because volatile simplifies encoding and the status flag does not depend on any other state within the program, it is well suited to use volatile.
A common feature of this type of status token is that there is usually only one state transition; TheshutdownRequestedflagfalseis converted totrue, and then the program stops. This pattern can be extended to a state flag that transitions back and forth, but can only be extended (fromfalseto, to, andtrueconverted) If the conversion period is not detectedfalse. In addition, some atomic state transition mechanisms, such as atomic variables, are also required.
Mode #2: One-time security release (one-time safe publication)
Lack of synchronization results in the inability to achieve visibility, which makes it more difficult to determine when to write object references instead of primitive values. In the absence of synchronization, you may encounter an updated value for an object reference (written by another thread) and the old value of the state of the object exists at the same time. (This is the source of the famous double-check lock (double-checked-locking) problem, where the object reference is read without synchronization, and the problem is that you might see an updated reference, but still see the incompletely constructed object through the reference).
One technique for implementing a secure publishing object is to define an object reference as a volatile type. Listing 3 shows an example where a background thread loads some data from the database during the startup phase. Other code will check whether the data has been published before it is used when it can take advantage of this data.
Listing 3. Use volatile variables for a one-time security release
public class BackgroundFloobleLoader {
public volatile Flooble theFlooble;
public void initInBackground() {
// do lots of stuff
theFlooble = new Flooble(); // this is the only write to theFlooble
}
}
public class SomeOtherClass {
public void doWork() {
while (true) {
// do some stuff...
// use the Flooble, but only if it is ready
if (floobleLoader.theFlooble != null)
doSomething(floobleLoader.theFlooble);
}
}
}
If thetheFlooblereference is not a volatile type, the code in the dereference willdoWork()theFloobleget an incomplete constructFlooble.
A prerequisite for this pattern is that the published object must be thread-safe or a valid immutable object (valid immutable means that the state of the object will never be modified after it is published). A reference to a volatile type ensures the visibility of the object's published form, but if the state of the object changes after it is published, additional synchronization is required.
Mode #3: Independent observation (independent observation)
Another simple pattern for safe use of volatile is to periodically "publish" the results for internal use by the program. For example, suppose there is an ambient sensor that can feel the ambient temperature. A background thread might read the sensor every few seconds and update the volatile variable that contains the current document. The other thread can then read the variable, allowing it to see the latest temperature values at any time.
Another application that uses this pattern is to collect statistics about the program. Listing 4 shows how the authentication mechanism remembers the name of the user who last logged on. Use the recurringlastUsercitation to publish the value for use by other parts of the program.
Listing 4. Use volatile variables for publication of multiple independent observation results
public class UserManager {
public volatile String lastUser;
public boolean authenticate(String user, String password) {
boolean valid = passwordIsValid(user, password);
if (valid) {
User u = new User();
activeUsers.add(u);
lastUser = user;
}
return valid;
}
}
This pattern is an extension of the preceding pattern, and a value is published to be used elsewhere within the program, but unlike the release of a one-time event, this is a series of independent events. This mode requires that the value being published is valid immutable-that is, the status of the value will not change after it is published. The code that uses this value needs to be clear that the value may change at any time.
Mode #4: "Volatile bean" mode
The volatile bean pattern applies to the framework that uses JavaBeans as the "honor structure." In volatile bean mode, JavaBean is used as a container for a set of independent properties that have getter and/or setter methods. The basic principle of the volatile bean pattern is that many frameworks provide containers for variable data holders (for exampleHttpSession), but the objects placed in those containers must be thread-safe.
In volatile bean mode, all data members of the JavaBean are volatile types, and getter and setter methods must be very common-no logic can be included except to get or set the corresponding property. In addition, for data members referenced by an object, the referenced object must be valid and immutable. (This disables the property with the array value, because when the array reference is declaredvolatile, only the reference, not the array itself, has the volatile semantics). For any volatile variable, the invariant or constraint cannot contain the JavaBean property. The example in Listing 5 shows a JavaBean that adheres to the volatile bean pattern:
Listing 5. The person object that adheres to the volatile bean pattern
@ThreadSafe
public class Person {
private volatile String firstName;
private volatile String lastName;
private volatile int age;
public String getFirstName() { return firstName; }
public String getLastName() { return lastName; }
public int getAge() { return age; }
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setAge(int age) {
this.age = age;
}
}
Advanced Mode of Volatile
The patterns described in the previous sections cover most of the basic use cases, and using volatile in these patterns is very useful and simple. This section describes a more advanced pattern in which volatile will provide performance or scalability benefits.
The advanced mode of volatile applications is very fragile. Therefore, the hypothetical conditions must be carefully proven, and these patterns are tightly encapsulated, because even very small changes can damage your code! Similarly, the reason for using more advanced volatile use cases is that it improves performance and ensures that this performance benefit is really determined before you begin applying advanced mode. These patterns need to be weighed to give up readability or maintainability in exchange for possible performance gains--if you don't need to improve performance (or you can't prove you need it with a rigorous test program), then this is probably a bad deal because you're likely to lose the candle, It is worth less than what you give up.
Mode #5: Low overhead read-write lock policy
So far, you should understand that volatile functionality is not enough to implement counters. Because++xit is actually a simple combination of three operations (read, add, store), if more than one thread happens to attempt to perform an incremental operation on the volatile counter at the same time, its updated value may be lost.
However, if the read operation is far more than the write operation, you can use the internal lock and the volatile variable to reduce the cost of the common code path. The thread-safe counters shown in Listing 6 use tosynchronizedensure that incremental operations are atomic and usevolatilethe visibility that guarantees the current results. This approach achieves better performance if the update is infrequent, because the cost of the read path involves only volatile read operations, which is usually better than the overhead of a non-competitive lock acquisition.
Listing 6. Using volatile and synchronized to achieve "less expensive read-write locks"
@ThreadSafe
public class CheesyCounter {
// Employs the cheap read-write lock trick
// All mutative operations MUST be done with the ‘this‘ lock held
@GuardedBy("this") private volatile int value;
public int getValue() { return value; }
public synchronized int increment() {
return value++;
}
}
This technique is called a "low-cost read-write lock" because you use a different synchronization mechanism for read and write operations. Because the write operation in this example violates the first condition that uses volatile, you cannot use volatile to safely implement counters--you must use locks. However, you can use volatile in a read operation to ensure the visibility of the current value, so you can use the lock to make all the changes, using volatile for read-only operations. Where the lock allows only one thread to access the value at a time, volatile allows multiple threads to perform read operations, so when using the volatile guarantee to read the code path, it is much more shared-as in read-write operations-than using locks to execute all code paths. However, it is important to keep in mind the weaknesses of this model: if you go beyond the most basic application of the pattern, it will become very difficult to combine these two competing mechanisms.
Conclusion
Volatile variables are a very simple, yet very fragile synchronization mechanism compared to locks, which in some cases provides better performance and scalability than locks. If you strictly follow the use of volatile conditions-that is, the variable is really independent of the other variables and its own previous values-in some cases you can use thevolatilesubstitutionsynchronizedto simplify the code. However,volatilethe code used is often more error-prone than code that uses locks. The pattern described in this article covers some of the most common use cases that can be usedvolatileinsteadsynchronized. Following these patterns (not exceeding their limits when using them) can help you implement most use cases safely and use volatile variables for better performance.
Java Threads: Atomic Atoms (RPM)
First, what is atomic?
The word atomic has something to do with atoms, which were thought to be the smallest unit of matter. The atomic in a computer means that it cannot be divided into parts. If a piece of code is considered atomic, it means that the code cannot be interrupted during execution. In general, atomic instructions are provided by the hardware for the software to implement the Atomic method (after a thread enters the method, it is not interrupted until it completes)
On the x86 platform, the CPU provides a means of locking the bus during instruction execution. The CPU chip has a lead #hlock pin, if the assembly language program in a command prefix "LOCK", after the assembly of the machine code so that the CPU in the execution of this instruction at the time of the #hlock pin to pull down, continue to the end of this instruction to release, So that the bus lock, so that the other CPU on the same bus temporarily can not access the memory through the bus, to ensure that this instruction in the multi-processor environment of atomicity.
Two, JDK1.5 's Atom pack: java.util.concurrent.atomic
This package provides a set of atomic classes. The basic feature is that in a multithreaded environment, when there are multiple threads executing the methods contained by instances of these classes at the same time, there is exclusivity, that is, when a thread enters the method and executes the instruction, it is not interrupted by another thread, and the other thread is like a spin lock until the method executes. It is only a logical understanding that the JVM chooses a second thread from the waiting queue to enter. It is actually implemented with hardware-related instructions that do not block threads (or simply block at the hardware level). The classes can be divided into 4 groups
- Atomicboolean,atomicinteger,atomiclong,atomicreference
- Atomicintegerarray,atomiclongarray
- Atomiclongfieldupdater,atomicintegerfieldupdater,atomicreferencefieldupdater
- Atomicmarkablereference,atomicstampedreference,atomicreferencearray
The role of the Atomic class
- Enables the manipulation of a single data to achieve atomicity
- Use the atomic class to build complex, block-free code
- Access to 2 or more of the atomic variables (or 2 or more 2 operations on a single atomic variable) is generally considered to be synchronous, so that these operations can be used as an atomic unit.
2.1 Atomicboolean, Atomicinteger, Atomiclong, atomicreference
These four basic types are used to handle Boolean, Integer, Long Integer, and object four kinds of data.
- Constructors (two constructors)
- Default constructor: The initialized data is False,0,0,null
- Parameter constructor: Data with parameters initialized
- Set () and Get () methods: Atomic data can be set and obtained atomically. Similar to volatile, ensuring that data is set or read in main memory
- Getandset () method
- The atom sets the variable to the new data while returning the previous old data
- It is essentially a get () operation and then a set () operation. Although these 2 operations are atomic, they are not atomic when they are merged together. At the level of Java's source program, it is not possible to do this without relying on the synchronized mechanism. You can only rely on the native method.
- Compareandset () and Weakcompareandset () methods
- Both of these methods are conditional modifier methods. These 2 methods accept 2 parameters, one is the expected data (expected), the other is the new data, and if the data in the atomic is consistent with the expected data, the new data is set to the atomic data, which returns true to indicate success, otherwise it is not set and returns false.
- For Atomicinteger, Atomiclong also offers some special methods. Getandincrement (), Incrementandget (), Getanddecrement (), Decrementandget (), Addandget (), Getandadd () to achieve some addition, subtract atomic operations. (Note that the-I, ++i is not an atomic operation, it contains 3 steps: The first step, read I; second step, add 1 or minus 1; step three: Write back memory)
2.1.1 1 examples-Creating a thread-safe stack with atomicreference
Java code
public class Linkedstack<t> {
Private atomicreference<node<t>> stacks = new atomicreference<node<t>> ();
Public T push (t e) {
Node<t> OldNode, NewNode;
while (true) {//The handling here is very special and must be the case.
OldNode = Stacks.get ();
NewNode = new Node<t> (e, OldNode);
if (Stacks.compareandset (OldNode, NewNode)) {
return e;
}
}
}
Public T pop () {
Node<t> OldNode, NewNode;
while (true) {
OldNode = Stacks.get ();
NewNode = Oldnode.next;
if (Stacks.compareandset (OldNode, NewNode)) {
return oldnode.object;
}
}
}
Private static Final class Node<t> {
Private T object;
Private node<t> Next;
Private Node (T object, node<t> next) {
This.object = object;
This.next = Next;
}
}
}
The difference between atomicreference and volatile