memory visibility

Visibility: When a thread modifies the state of an object, other threads can see the state change that occurs. This situation cannot be achieved without synchronization.

The code below illustrates the error that occurs when multiple threads share data without synchronization. In the code, both the main thread and the reader thread access the shared variables ready and number. Obviously the code looks like it will output 42, but in fact Ken might output 0 (resulting from the reordering), or even fail to terminate at all. This is because there is no sufficient synchronization mechanism in the code, so there is no guarantee that the main thread writes ready and number to the reading thread.

public class NoVisibility{
    private static bolean ready;
    private static int number;  

    private static class ReaderThread extends Thread{
        public void run(){
            while(!ready)
                Thread.yield();
            System.out.println(number);
        }
    }

    public static void main(String[] args){
        new ReadThread().start();
        number = 42;
        ready = true;
    }
}

Failure data:

When a reading thread looks at the ready variable, it may get an invalid value. Unless you use synchronization every time you access a variable, you are likely to get an invalid value for that variable.

The following code is prone to stale value problems: if one thread calls set, another thread that is calling get may or may not see the updated value.

public class MutableInteger{
    private int value;
    public int get(){ return value; }
    public void set(int alue) { this.value = value; }
}

Changing the above code to a thread-safe class, just synchronizing the set method is not enough, the thread calling get will still see the invalid value.

public class MutableInteger{
    @GuardedBy("this") private int value;

    public synchronized int get(){ return value; }
    public synchronized void set(int alue) { this.value = value; }
}

Non-atomic 64-bit operations:

Minimum safety: When a thread reads a variable without synchronization, it may get an invalid value, but at least the value is a value set by some thread, not a random value. This security is called minimum security.

Minimum safety applies to the vast majority of variables, with one exception: non-volatile 64-bit numeric variables (long and double). The Java memory model requires that reads and writes of variables must be atomic operations, but for non-volatile 64-bit numeric variables, the JVM allows the 64-bit operation to be split into two 32-bit operations. Then if reading and writing to the variable in multiple threads are performed in different threads, it is likely to read the upper 32 bits of one value and the lower 32 bits of another value. Even without considering the problem of invalid data, it is not safe to share 64-bit type data in multiple threads, unless volatile is declared or locked.

Locking and Visibility:

When thread B executes the code protected by the lock, it can see the results of all previous operations of thread A in the same synchronized code block. Without synchronization, the above guarantees cannot be achieved.

It is now understandable why all threads are required to synchronize on the same lock when accessing a shared and mutable variable, in order to ensure that changes to the variable by one thread are visible to other threads. Otherwise a thread reading a variable without holding the correct lock may read an invalid value.

Volatile variable:

Volatile variables are a slightly weaker synchronization mechanism that ensures that other threads are notified of variable updates. When a variable is declared volatile, the variable is shared, and the compiler does not reorder operations on the variable with other memory operations. Volatile variables are also not cached in registers or invisible to other processors, so reading a volatile variable always returns the most recently written value.

Volatile variables should only be used when they simplify the implementation of the code and the verification of synchronization strategies. Correct usage of Volatile variables includes:

  • ensure visibility of their own state;
  • ensure the visibility of the state of the objects they refer to;
  • Mark the occurrence of some important program life cycle events.

The following code is the most classic use of volatile: checking a state flag to determine whether to perform an operation (singleton pattern):

public class Singleton{
    private volatile static Singleton uniqueInstance;
    private Singleton(){}//构造器定义为私有 
    
    public static Singleton getInstance(){
        if(uniqueInstance==null){ //检查实例,不存在则进入同步块 
            synchronized (Singleton.class){ 
                if(uniqueInstance==null){ //进入同步块后再次检查 
                    uniqueInstance = new Singleton(); 
        return uniqueInstance;
}

The locking mechanism can guarantee both visibility and atomicity, while volatile variables can only guarantee visibility.

Many people think: "volatile variables are immediately visible to all threads, and modifications to volatile variables are immediately reflected in other threads, so operations based on volatile variables are thread-safe". This sentence is partly true in its arguments, but its arguments do not lead to its conclusion. Volatile variables do not have inconsistencies in each thread (volatile in the working memory of each thread can be inconsistent, but since each use will be refreshed first, the execution engine cannot see the inconsistency), but the operation in Java is non-atomic, Causes volatile variables to be unsafe in concurrent situations.

Since volatile variables can only guarantee visibility, in operation scenarios that do not meet the following two rules, it is still necessary to ensure atomicity by locking (using synchronized or the atomic class in java.util.concurrent):

  • The result of the operation does not depend on the current value of the variable, or it is guaranteed that only a single thread modifies the value of the variable;
  • Variables do not need to participate in invariant constraints with other state variables.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325249775&siteId=291194637