[Multithreading]-Characteristics of the volatile keyword

1. What is the volatile keyword

In daily interviews, volatile is undoubtedly a problem that is mentioned very frequently. The major public accounts are also full of analysis of the volatile keyword. So what is volatile? ? ? Where are they used? ? ?

First of all, in terms of understanding keywords, I think that understanding the Chinese meaning of this keyword helps us remember. Then open Baidu Translate to search for volatile and you can get the following results:
Insert picture description here
Through Baidu Translate, you can know that volatile is an adjective, which is mainly used to form Things that are easy to change, as a keyword of the Java language, volatile is often used to modify a variable parameter. Then we have to figure out the next question is what changes will be produced by volatile modified variables? That is, what is the role of the volatile keyword?

Two, the introduction of volatile

Not much to say, let’s look at a demo first:

public class VolatileTest {
    
    
	private static boolean flag;
	// 违反可见性验证
	public static void main(String[] args) {
    
    
		flag = true;
		new Thread(() -> {
    
    
			try {
    
    
			//通过线程睡眠达到延时修改flag标记的作用
				Thread.currentThread().sleep(10l);
			} catch (InterruptedException e) {
    
    
				e.printStackTrace();
			}
			flag = false;
		}).start();
		System.out.println("启动循环");
		// 如果上面flag修改生效 程序将退出循环
		while (flag) {
    
    
		}
		System.out.println("退出循环");
	}
}

Experimental results: The
Insert picture description here
above experimental results can be seen that although the newly created thread has changed the value of flag, the main thread does not perceive it, so it cannot exit the loop. The point here is that there should be no code in the while loop, otherwise it will affect the result. So what should we do to make the main thread perceive that the flag is worth changing? You don’t have to guess. This chapter is about volatile. The above changes are made as follows when declaring variables:

	private  volatile static boolean flag;

Yes, you are not mistaken. You only need to add a word to change the running result of the program:
Insert picture description here
here we can see that the program has exited normally, so what did volatile do here? ?

Three, the characteristics of volatile

1. Volatile and visibility

What is visibility ? The simple explanation of additivity is that when a thread changes a shared variable, other threads can learn the process of the variable change and the latest state of the variable.
In the above Demo, we added the volatile keyword to the shared variable flag. When a thread is trying to change the flag, the JVM will notify other threads that the variable cache they previously read has expired and needs to go to the shared memory area. Take the latest variable value.

About the realization principle of volatile The
question of how threads notify each other to update the cache involves some knowledge of computer principles. If you are interested, you can learn about the memory barrier and the cache coherency protocol of the CPU, so I won’t go into details here.

2. Volatile and atomicity

We have proved that volatile has visibility through the previous Demo, so does the atomic volatile support among the three features of concurrent programming? Not much to say, go to DEMO:

public class VolatileAtomicity {
    
    
	public volatile int count = 0;

	private void addCount() {
    
    
	//count 自增
		for (int i = 0; i < 100; i++) {
    
    
			count++;
		}
		System.out.println("count = " + count);
	}

	public static void main(String[] args) {
    
    
		VolatileAtomicity volatileAtomicity = new VolatileAtomicity();
		// 循环创建1000个新线程 每个线程count自增100次 期望结果为100000
		for (int i = 0; i < 1000; i++) {
    
    
			new Thread(() -> {
    
    
				volatileAtomicity.addCount();
			}).start();
			;
		}
	}
}

Expected result: 100000
Operation result:

Insert picture description here
Here we see that although we added the volatile keyword modification to the shared variable count, the final running result did not meet our expectations. Why? In fact, the main reason is that count++ disassembled him when the jvm was parsed:

  1. First read the value of count. At this time, because volatile has visibility, the value of count is the latest value at this time
  2. At this time, the value of count is calculated. It should be noted that other threads may have modified the value of count at this time, so the value of count+1 at this time has violated the thread safety policy.
  3. Re-assign the result of count+1 to count. At this time, the value of count has deviated from the expectation
    . As can be seen from the above example, volatile can only guarantee visibility when the read is worthwhile, but it cannot guarantee concurrency during subsequent operations. Thread safety issues caused by threads, so volatile is not atomic .

Here we need to make a supplementary explanation on the visibility of volatile : The visibility of volatile is reflected in the fact that when the thread reads the variable, the latest variable is obtained, and the latest variable will not be obtained when the operation is performed after reading. This Part of the conceptual design of memory read and write barriers, interested students can Baidu by themselves.

3. Volatile and orderliness

Before understanding the order, we must first understand a concept that is the instruction reordering of the CPU

When the CPU executes the code, it does not strictly execute the code in the order in which it is written. Instead, it executes the instructions after appropriate reordering under the principle of as-if-serial. This is mainly In order to improve the execution efficiency of the CPU and reduce the idle time.

So the first thing we have to do is to prove that the CPU has instruction reordering. Let’s not talk about Demo:

public class VolatileOrderliness {
    
    

	private static long a = 0;
	private static long b = 0;
	private static long c = 0;
	private static long d = 0;
	private static long e = 0;
	private static long f = 0;
	private static long g = 0;
	private static long h = 0;

	public static long count = 0;

	public static void main(String[] args) {
    
    
		// 由于cpu指令重排发生存在概率 所以使用死循环调用 然后再出现的时候通过break跳出循环
		for (;;) {
    
    
			a = 0;
			b = 0;
			c = 0;
			d = 0;
			e = 0;
			f = 0;
			g = 0;
			h = 0;
			count++;
			Thread t1 = new Thread(() -> {
    
    
				a = 1;
				c = 101;
				d = 102;
				g = b;
			});
			Thread t2 = new Thread(() -> {
    
    
				b = 1;
				e = 201;
				f = 202;
				h = a;
			});
			t1.start();
			t2.start();
			try {
    
    
				t1.join();
				t2.join();
				String result = "count = " + count + " g = " + g + ", h=" + h;
				if (g == 0 && h == 0) {
    
    
					// 当g 和h 都出现0的时候 一定是发生了指令重排序
					System.err.println(result);
					break;
				} else {
    
    
					System.out.println(result);
				}
			} catch (InterruptedException e1) {
    
    
				e1.printStackTrace();
			}

		}

	}

}


Operation results:
Insert picture description here
First, we analyze the Demo. Whether thread t1 is executed first or thread t2 is executed first, their first line of code is assigned a value of 1 to a and b, which means that under normal code logic, as each The assignment g and h last executed by a thread must have a value of 1, but at this time the values ​​of g and h are both 0, indicating that the CPU disrupted the execution order of the code and reordered it when executing the code. As follows: The
Insert picture description here
Insert picture description here
CPU switches the assignment operation of g and h to the assignment operation of a and b. It is actually very simple to solve this problem. We only need to add the volatile keyword to a and b.

Four, volatile interview related

Here I also summarized some common volatile interview questions to share with you.

1. Talk about which scenarios are usually applied to volatile.
Answer: The simplest application scenario of volatile is to use it in singleton objects implemented in double-check-lock mode. In DCL singleton mode, volatile is mainly used to prevent threads from getting it. Semi-initialized variables caused by instruction reordering.
2. What are the similarities and differences between volatile and synchronized?
Answer:
1) Volatile is mainly used to modify variables, while synchronized is mainly used to modify code blocks.
2) Volatile cannot guarantee atomicity, synchronized can
3) Volatile will not cause thread blockage, synchronized will cause

Okay, if you have any questions or think the author has something wrong, please leave a message!
good luck!

Guess you like

Origin blog.csdn.net/xiaoai1994/article/details/110437416