I JMM memory model
1.1 what is the JMM memory model
When multiple threads use the same shared variable:
Step 1: copy a copy of the shared variable from the main memory to your own working memory, and modify the copy of the shared variable in your own working memory.
Step 2: after modifying the value of the shared variable, write the new value back to the main memory.
Step 3: after the value of the shared variable in the main memory is modified, notify other threads in time to update the value in their working memory and modify it to the latest value in the main memory.
This "timely notification" mechanism is the visibility of JMM memory model. (the modification of the value of the shared variable in the main physical memory is visible to all threads in time.)
1.2 what is volatile keyword
volatile keyword is a lightweight synchronization mechanism provided by java virtual machine;
volatile keyword has three features: ensuring visibility, not ensuring atomicity, and prohibiting instruction rearrangement.
II volatile keyword ensures visibility
2.1 meaning of visibility:
In java, each thread will have its own workspace when working. For shared variables between threads, threads operate them in this way. First, copy a copy of the value of the shared variable from main memory to their own workspace, and then operate the copy in their own workspace, Then every time the value of the replica is modified, it will immediately write the value of the replica back to the main memory and update the value of the shared variable in the main memory. Then the main memory will immediately notify other threads to update the value of the shared variable. This process realizes the visibility of the JVM; To sum up, the modification of shared variables in the main thread is visible to all threads in time.
2.2 code verification
package InterviewTest; class MyData{ volatile int number = 0; public void addTo60() { this.number = 60; } } public class VolatileDemo { public static void main(String[] args) { MyData myData = new MyData(); new Thread(()->{ System.out.println(Thread.currentThread().getName()+" come in"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } myData.addTo60(); System.out.println(Thread.currentThread().getName()+ " updated number value:"+myData.number); },"VolatileDemo").start(); /*If the volatile keyword is not added to number, modify number in the VolatileDemo thread, * The result is invisible to the main thread; number was originally 0, but it was changed to 60 in the working memory of VolatileDemo thread, * Then the modified value is written back to the main thread, but the visibility is not realized, and the main thread cannot be notified to modify its working memory in time * So in the working memory of the main thread, the value of number is still 0, * Therefore, the judgment condition of the following while loop is always true and cannot exit the loop. * After adding the volatile keyword to number, the VolatileDemo thread modifies the value of number in the working memory * Write it back to the main memory, and then immediately tell the main thread to modify the value of number in its working memory, * Then you can modify the shared variables in the VolatileDemo thread, which can be seen in other threads, * This is the visibility of JMM.! */ while(myData.number==0) {} System.out.println(Thread.currentThread().getName() +" mission is over:"+myData.number); } }
III volatile keyword does not guarantee atomicity
3.1 what does atomicity mean?
Indivisibility, integrity, that is, when a thread is doing a specific business, the middle cannot be blocked or divided. The whole needs to be complete, either succeed at the same time or fail at the same time.
3.2 verification:
package InterviewTest;
import java.util.concurrent.atomic.AtomicInteger;
/*
- volatile cannot guarantee the verification of atomicity
*/
class MyData2{
volatile int number = 0;//synchronized
public void addTo60() { this.number=60; } public void addPlus() {//synchronized number++; } AtomicInteger atomicInteger = new AtomicInteger(); public void addAtomic() {//Automatic growth of atomicity 1 //Atomically increments by one the current value. atomicInteger.getAndIncrement(); }
}
public class VolatileDemo2 {
public static void main(String[] args) {
MyData2 myData = new MyData2();
for(int i=0;i<20;i++) { new Thread(()-> { for(int j=1;j<=1000;j++) { myData.addPlus(); myData.addAtomic(); } },"Thread:"+String.valueOf(i)).start(); } //You need to wait until all the above 20 threads are calculated, and then use the main thread to obtain the final result to see how many the results are while(Thread.activeCount()>2) {//Judge whether the above 20 threads have been executed Thread.yield();//Comity thread; The main thread allows other threads to execute } System.out.println(Thread.currentThread().getName() +" int finally number value:"+myData.number); System.out.println(Thread.currentThread().getName() +" AtomicInteger finally number value:"+myData.atomicInteger); }
}
How to solve the problem that volatile keyword does not guarantee atomicity:
(1) Use the AtomicInteger type in JUC directly.
(2) Add the synchronized keyword to the method definition
IV Prohibition of instruction rearrangement of volatile keyword
No, because there are "data dependencies", y and x