This is the first day of my participation in the August Text Challenge.More challenges in August
Concurrency is when multiple threads access the same resource. Parallel, all sorts of things going on at the same time.
Volatile is a lightweight synchronization mechanism provided by the Java Virtual machine
Three features, guaranteed visibility, not guaranteed atomicity, forbid instruction reordering
The Java Memory Model (JMM) is an abstract concept that doesn’t really exist. It describes a set of rules or specifications that define how variables in a program (including instance fields, static fields, and elements that make up array objects) can be accessed.
JMM rules on synchronization:
- 1. Before the thread is unlocked, the value of the shared variable must be refreshed back to the main memory.
- 2. Before the thread locks, it must read the latest value of main memory into its own working memory
- 3. Lock to unlock is the same lock
It requires visibility, atomicity, and order, but volatile guarantees only two, so volatile is a lightweight synchronization mechanism.
Because the JVM to run the program of the entity is a thread, and every thread creation when the JVM to create a working memory (called the stack space) in some places, the working memory is the private data area each thread, while the Java memory model that all variables are stored in main memory, main memory is Shared memory region, all threads can access, But the thread to the operation of the variable (reading assignment, etc.) must be carried out in the working memory and variable from the main memory of the copy to their working memory space, and operation, the variable operation to complete before you write variables back to main memory, cannot be directly operating variables in main memory, working memory storage in various threads of variables in the main memory copy of the test, Therefore, different threads cannot access each other’s working memory, and communication between threads (passing values) must be done through main memory, as shown in the figure.
Volatile Ensures visibility. Data is stored in main memory. When threads want to change data in main memory, they copy the data in main memory to their working memory, write the changes back to main memory, and other threads obtain the latest data from main memory. Code validation
class MyData{ volatile int number=0; public void addNumber(){ this.number=60; } } public class voliteDemo{ public static void main(String args[]){ MyData myData = new MyData(); new Thread(()->{ System.out.println(Thread.currentThread().getName()+"\t come in"); try{ TimeUnit.SECONDS.sleep(3); }catch(InterruptedException e){ e.printStackTrace(); } myData.addNumber(); System.out.println(Thread.currentThread().getName()+"\t update number value" + myData.number); },"AAA").start(); while(myData.number==0){} System.out.println(Thread.currentThread().getName()+"\t main thread vaule "+ myData.number); }}Copy the code
In summary, thread AAA and main thread are two threads. If thread AAA makes a volatile change, the main thread is unaware of the change unless it makes a volatile change. If thread AAA makes a volatile change, the main thread is aware of the change. Add the volatile keyword modifier to ensure visibility.
Volatile does not guarantee atomicity. What is atomicity? Indivisibility. Integrity.
class MyData{ volatile int number = 0; public void addPlusPlus(){ number++; } } class MyDataDemo{ public static void main(String args[]){ MyData myData = new MyData(); for(int i =1; i<=20; i++){ new Thread(()->{ for(j=1; j<=1000; j++){ myData.addPlusPlus(); } },String.valueof(i)).start(); } while(Thread.activeCount() >2){ Thread.yield(); } System.out.println(Thread.currentThread().getName()+"\t number value "+myData.number); }}Copy the code
The value of number will be less than 20000 after the run, indicating that volatile does not guarantee atomicity. Multithreaded access methods, there will be data loss. I’ll show you why atomicity is not guaranteed, because when multiple threads modify a variable in main memory, they copy the variable from main memory to their working memory, and then write it back to main memory. When a thread changes the value of a variable, it writes back to main memory with the same value as any other thread writes back to main memory, so it overwrites and loses data. To ensure atomicity, use synchronized, but synchronization is too heavy to recommend. You can use AtomicInterger. Modify the code
class MyData{ volatile int number =0; AtomicInteger atomicInteger = new AtomicInteger(); public void addAtomic(){ atomicInteger.getAndIncrement(); } } public class VolatileDemo{ public static void main(String args[]){ MyData myData = new MyData(); for(inti=0; i<20; i++){ new Thread(()->{ for(j=1; j<=1000; j++){ myData.addAtomic(); } },String.valueof(i)).start(); } while(Thread.activeCount() >2){ Thread.yield(); } System.out.println(Thread.currentThread().getName()+"\t number value "+myData.atomicInteger); }}Copy the code
At this point, atomicity can be guaranteed. Why adding atomic can solve atomicity? CAS is used.
Volatile, prohibiting instruction reordering. In the case of multithreading, the source code may not be written in the same order as the compiler. When single threaded, ensure consistency between the final execution of the program and the sequential execution of the code. Source code -> rearrangement of compiler optimizations -> rearrangement of instructions parallel -> rearrangement of memory systems -> instructions finally executed.
int a,b,x,y = 0; Thread 2 x=a; y=b; b=1; a=2; Output x=0 y=0 if the compiler rearranges this code, thread 1 thread 2 b=1; A = 2; X = a; Y = b; Output x= 2, y=1;Copy the code
The compiler modifies the order of execution, giving different results. The compiler then does not reorder the variable after using the volatile keyword.
Thread safety is guaranteed:
The visibility problem caused by delays in synchronizing data from working memory to main memory can be solved by using the synchronized and volatile keywords, both of which make changes made by one thread immediately visible to other threads.
Visibility problems caused by instruction reordering can be solved by using the volatile keyword because volatile can disallow instruction reordering.
Where volatile is used:
Singleton pattern, using double judgment, instruction reordering occurs, and volatile disallows instruction reordering.
CAS source code, JUC packages used extensively in volatile.