1-volatile keyword memory visibility

Suppose there is a variable a=1 in the memory; a new value of a needs to be calculated. 

In this process: It is operated by CPU operation. The calculation process is to make a copy of the variable a from the memory and then after the calculation, the returned value is rewritten into the variable a in the memory. (CPU is used for calculation, and memory is used for storage. This feature causes the calculation process to copy a copy of data to the cpu calculation, and then return the result to the memory)

The above process is equivalent to a thread operation. When there are multiple threads performing operations, some problems will occur:

Since each thread copies a copy of data instead of directly operating data in memory, there are the following problems 

Memory Visibility:

         A memory reads the memory data, calculates, and rewrites it to the memory. At this time, other threads can immediately see the state changes that occur. Memory visibility

Visibility error: refers to when the read operation and the write operation are executed in different threads, it is impossible to ensure that the thread performing the read operation can see the value written by other threads in a timely manner. Sometimes it is even impossible.

Memory visibility and visibility errors are often encountered during multi-threaded development

At this point, synchronization can be used to ensure that the object is released safely. In addition, we can also use a more lightweight volatile variable

 

Introduction of Volatile keywords:

Java provides a weaker synchronization mechanism, namely volatile variables, to ensure that other threads are notified of variable update operations.
You can think of volatile as a lightweight lock,

But it is somewhat different from locks:

  1. For multithreading, it is not a mutually exclusive relationship
  2. "Atomic operation" of variable state cannot be guaranteed

The role of volatile: to solve the invisible problem of multiple threads accessing data memory

Code example:

There is a class below that implements the Runnable interface, and the run method modifies the value of flag

class ThreadDemo implements Runnable {

	private boolean flag = false;//未加任何修饰

	@Override
	public void run() {
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
		}

		flag = true;//修改flag的值
		//修改成功后输出flag的值
		System.out.println("flag=" + isFlag());//调用isFlag返回flag值,并打印出来

	}

	public boolean isFlag() {
		return flag;
	}


}

 Execute the main method of TestVolatile below

public class TestVolatile {
	
	public static void main(String[] args) {
		ThreadDemo td = new ThreadDemo();
		new Thread(td).start();// 启动线程会执行run方法,然后this.flag=true
		
		while(true){
			if(td.isFlag()){//如果前面的线程修改flag成功并且通过isFlag返回为true则打印分割线
				System.out.println("------------------");
				break;//结束循环
			}
		}
		
	}

}

The results obtained:

new Thread(td).start(); here will output a flag=true

Then the if(td.isFlag()) in the following while(true) will always get false, resulting in an endless loop and the code in the dividing line will not be executed.

Why it came out like this? Didn't the previous thread have modified the value of flag (flag=true)? Then isFlag() doesn't return true?

Then it will execute System.out.println("------------------") and break to end the loop?

The gif of the execution result is as follows, only the output flag=true will not have a dividing line ----------------

Have you already wondered?

Answer the reason: drawing description

As shown in the figure, the first thread modifies the flag value and will modify the flag value in the main memory, but in the while loop, it is not known that the thread modifies the flag because it has a cache, and the flag in the cache is always false, so it will always Endless loop.

So how to solve the problem?

Method 1: Add a synchronized (td) to lock the td class to solve it, that is, in the while loop as shown in the figure

		while(true){
			synchronized (td){
				if(td.isFlag()){
					System.out.println("------------------");
					break;
				}
			}

		}

The execution effect after adding synchronized (td) is as shown in the gif picture below

Method two, add a volatile modifier to the flag variable

That is, add a volatile before the variable

 

So the question is coming? Which way is better? What are the advantages and disadvantages of the two methods?

In the code above

Method one uses synchronized to lock the td object, so every time you enter the while loop, you need to go to the main memory to determine whether the td object has a lock. At this time, the cache will be refreshed from the main memory. If the lock is otherwise If a thread operates on the td object, it will determine whether there is a lock, and if there is a lock, it will block. Very low efficiency

Volatile is equivalent to directly operating the flag object in the main memory to ensure that the data in the memory of multiple threads is visible.

Volatile will cause the underlying reordering to fail. The efficiency is relatively low (compared to the time when none of the above is added), but it is more efficient than the locking method

So can volatile be used in any situation? The answer is no

Because volatile does not have "mutual exclusion" and "atomicity", while locks have mutual exclusion and atomicity

 

Guess you like

Origin blog.csdn.net/qq_41813208/article/details/103519089
Recommended