Volatile is often used to implement thread-safe code (although many people are afraid to use it because they don’t know it), but volatile itself is not thread-safe and is more restricted to specific scenarios than synchoronized. The purpose of this article is to give you a sense of the nature of volatile, and to do so, we’ll look at Java’s memory model and memory management for variable operations (never fear, you’ll find it easy).
First, memory model
Memory can be divided into two simple types: working memory and main memory. All data ultimately needs to be stored in main memory, and working memory is unique to the thread, with no interference between threads. Java’s memory model mainly defines the interaction between working memory and main memory, that is, how the working memory copies data from main memory and writes data to it. Java defines eight atomic operations to accomplish working memory interaction with main memory:
- Lock makes objects thread-exclusive
- Unlock releases the lock of an object in a thread-exclusive state
- Read Reads data from main memory
- Load writes data read from main memory to working memory
- Use Working memory usage object
- Assign assigns to objects in working memory
- Store transfers objects from working memory to main memory
- Write Writes objects to main memory and overwrites the old values
These operations are also conditional: read and load, store and write must be paired, that is, data working memory must accept data read from main memory; Data passed to main memory cannot be denied writes. New variables can only be created in main memory, and objects that have not been initialized cannot be used in working memory. Objects that have not been initialized can only be locked by one thread. And can be this thread lock is not locked object many times are not allowed to perform unlock operation on an object before unlock, must write object back to the main memory Java 8 kinds of atomic operations, has certain constraints before each other, but is not strictly limited any two operations must be continuous, just said come in pairs, This is where thread insecurity comes in. With that background in mind, let’s look at how volatile variables differ from normal variables
Volatile and common variables
2.1 Security of Volatile
Let’s use an example to illustrate the difference between volatile variables and common variables. Suppose there are two threads operating on a main-memory object and thread 1 starts earlier than thread 2 (for example, an a++ operation).
public class ThreadSafeTest {
public static int a = 0;
public static void increase() {
a++;
}
public static void main (String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for(int j = 0; j < 100; j++) { increase(); }}}); Thread t2 = new Thread(newRunnable() {
@Override
public void run() {
for(int j = 0; j < 100; j++) { increase(); }}}); t1.start(); t2.start(); }}Copy the code
When thread 2 reads the main memory object (a), it can happen in several periods: before read, after read, after Load, after use, after assign, after Store, after write (as shown below).
Let’s say thread 1 executes a++,a changes from 0 to 1 before writing back to the main memory object, and thread 2 reads data from the main memory object a=0; If thread 1 writes to main memory a=1, thread 2 still executes a++, which is still equal to 1 (should be equal to 2).
Is it correct to make A volatile? Volatile imposes stricter restrictions on what objects can do:
- Use is not preceded by read and load
-
Assign must be followed by store and write, essentially turning the three atomic operations read Load use into one atomic operation; Make assign-store-write an atomic operation. Many articles have stated that volatile is visible to all threads, meaning that it writes back to main memory immediately after executing the assign; Whenever a main memory object is read by any thread, main memory is flushed. Data consistency is represented in main memory, but not necessarily in thread memory. Volatile public class ThreadSafeTest {public static volatile int a = 0;
public static void increase() {
a++;Copy the code
}
public static void main (String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for(int j = 0; j < 100; j++) { increase(); }}}); Thread t2 = new Thread(newRunnable() {
@Override
public void run() {
for(int j = 0; j < 100; j++) { increase(); }}}); t1.start(); t2.start(); }Copy the code
}
‘ ‘found after running, also can not get the correct result (if get j, please increase the value). Fuck you, what happened to thread-safe variables? Why is that not true? This is because the threads inside the data inconsistency is still possible, for example, if the thread 2, while reading the data in the thread 1 after use, but the thread 1 at this time has not yet had time to write back cache, then thread 2 use the data still is zero, two threads of 0 + + at the same time, the result can only be 1, rather than the ideal 2.
2.2 Thread-safety of volatile is conditional
If volatile is not thread-safe, what is the use of it? If you’ve read my article on thread-safety, you know that all objects are relatively thread-safe, that is, conditional. The thread-safety benefits of volatile, of course, are conditional, as they complement the weight of synchronized thread synchronization and generally outperform synchronized. What are the thread-safe conditions for volatile? Which scenarios are suitable for use? The Java Virtual Machine provides two conditions:
- The result of the operation does not depend on the current value of the variable (that is, the result does not depend on producing an intermediate result), or can ensure that only a single thread changes the value of the variable
- Variables do not need to participate in immutable constraints with other state variables (I think this is redundant, this other variable must also be thread safe).
What kind of scenarios does that work for? This I am not an example, a brother summed up very well, the reference is as follows: www.ibm.com/developerwo…