An example of synchronized and volatile for JAVA multithreading

In multithreading, when it comes to thread safety and thread synchronization, we often think of two keywords: volatile and synchronized, so what is the difference between the two?

1. volatile-modified variables have visibility

volatile is a variable modifier, and the modified variable has visibility.

Visibility means that once a thread modifies the volatile-modified variable, it will ensure that the modified value will be updated to the main memory immediately. When other threads need to read it, the modified value can be obtained immediately.

In order to speed up the running efficiency of the program in Java , the operation of some variables is usually carried out on the register of the thread or the CPU cache, and then it will be synchronized to the main memory, while the variable with the volatile modifier is directly Read and write main memory.

2. volatile prohibits instruction rearrangement

volatile can disable instruction reordering.

Instruction rearrangement means that the processor may optimize the input code in order to improve the efficiency of the program. It does not guarantee that the execution order of each statement is the same as the order in the code, but it will guarantee the final execution result of the program and the result of the code sequence execution. is consistent. Instruction reordering will not affect the execution of a single thread, but it will affect the correctness of concurrent execution of threads.

When a program executes a read or write operation to a volatile-modified variable, the previous operation must have been completed, and the result has been visible to the subsequent operation, and the subsequent operation must not have been performed.

3. synchronized

Synchronized can act on a piece of code or method, which can ensure both visibility and atomicity.

Visibility is reflected in: synchronized or Lock can ensure that only one thread acquires the lock at the same time and then executes the synchronization code, and the changes to variables are flushed to main memory before releasing the lock.

Atomicity is expressed in: either do not execute, or execute to the end.

4. Summary

(1) So we can see that although volatile has visibility, it does not guarantee atomicity.

(2) In terms of performance, the synchronized keyword is to prevent multiple threads from executing a piece of code at the same time, which will affect the program execution efficiency, and the volatile keyword has better performance than synchronized in some cases.

However, it should be noted that the volatile keyword cannot replace the synchronized keyword, because the volatile keyword cannot guarantee the atomicity of the operation.

See the following example:

public class VolatileTest {
    public volatile int inc = 0;
    public void increase() {
        inc++;
    }
 
    public static void main(String[] args) {
        final VolatileTest test = new VolatileTest();
        for(int i=0;i<10;i++){
            new Thread(){
                public void run() {
                    for(int j=0;j<1000;j++)
                        test.increase();
                };
            }.start();
        }
 
        while (Thread.activeCount()>2)   // Ensure that all previous threads have finished 
            executing Thread.yield();
        System.out.println(test.inc);
    }
}

In the example, 10 new threads are used, and the increase() method is called 1000 times respectively. The result of each operation is inconsistent, and it is a number less than 10000. The auto-increment operation is not an atomic operation, and volatile cannot guarantee atomicity. Use volatile to modify the int variable i, and multiple threads perform i++ operations at the same time. For example, two threads A and B perform i++ operations on volatile-modified i. The initial value of i is 0. When thread A executes i++, it just reads the value of i, 0, and switches to thread B. Thread B (from memory ) The value of i is also read as 0, and then it switches to the A thread to continue the i++ operation. After completion, i is 1, and then switches to the B thread. Because it has been read before, it continues to execute the i++ operation. The final result i is 1. The same can explain why the result of each run is a number less than 10000.

However, using synchronized to modify part of the code as follows can ensure that only one thread acquires the lock at the same time and then executes the synchronized code. The result of the operation must be 10000.

public  int inc = 0;  
public synchronized void increase() {  
        inc++;  
}

 

Guess you like

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