Java Concurrent Programming volatile keyword

Keywords: Java

;

volatile understanding

Java language supports multithreading. In order to solve the problem of thread concurrency, synchronization block and volatile keyword mechanism are introduced. Volatile has the "visibility" of synchronized keyword. For each use of volatile variable, the thread can get the latest value of current volatile variable, but there is no "concurrency correctness" of synchronized keyword, that is to say, the order of thread execution is not guaranteed.

characteristic

1. Ensure memory visibility

Each thread's operations on shared variables in main memory are copied to its own working memory and then written back to main memory. It is possible that when thread AAA modifies the value of shared variable x and has not yet written it back to main memory, another thread BBB operates on A shared variable x in memory, but at this time, the sharing in working memory of thread A is not invisible to thread B compared with that of thread X. This phenomenon of synchronization delay between working memory and main memory causes visibility problems. Java provides volatile to ensure visibility. When A variable is modified by volatile, it means that the thread local memory is invalid. When A thread modifies the shared variable, it will be immediately updated to main memory. When other threads read the shared variable, it will be directly read from main memory.

2. No guarantee of atomicity

Atomicity is not interruptible in an operation. Either all operations succeed or all operations fail. For example, a + +, a+=1 is not an atomic operation, and volatile cannot guarantee atomicity.

3. Disallow instruction reordering

In order to improve performance, compilers and processors often rearrange instructions when executing programs
1. Ensure that the final execution result of the program is consistent with the sequential execution result of the code in a single thread environment
2. The processor must consider the data dependency between instructions when reordering
3. In the multithreaded environment, because of the existence of compiler optimization and rearrangement, it is uncertain whether the variables used by the two threads can be consistent, and the result cannot be predicted

code

Ensure memory visibility

package com.raicho.mianshi.myvolatile;

public class MyVolatileVisibility {

 //   private int i;
    private volatile int i;

    public void changeI(int i) {
        this.i = i;
    }

    public static void main(String[] args) {
//        System.out.println("no volatile keyword added");
        MyVolatileVisibility myVolatile = new MyVolatileVisibility();

        new Thread(() -> {

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            myVolatile.changeI(1);
            System.out.println(Thread.currentThread().getName()+"Modified i="+myVolatile.i);
        },"Thread 1 ").start();

        System.out.println( Thread.currentThread().getName()+"visit i = "+ myVolatile.i);
        while (myVolatile.i == 0) {

        }


    }


}

 

When the volatile keyword is not added, when the main thread first accesses the value of i as 0, then thread 1 modifies i=1, and returns to the mian thread, it cannot be detected that the value of i is changed to 1. However, the while loop cannot be ended all the time. With the volatile keyword, it can be detected that other threads have modified the value of i to 1, and the program ends.

No guarantee of atomicity

package com.raicho.mianshi.myvolatile;

public class VolatileAtomicity {
    volatile int number = 0;

    public void addNum(){
        number++;
    }

    public static void main(String[] args) {
        VolatileAtomicity va = new VolatileAtomicity();

        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    va.addNum();
                }
            },String.valueOf(i)).start();
        }

        //Wait for 20 threads to finish
        while (Thread.activeCount() >2){
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName()+" number = "+va.number);
    }
}

 

After code verification, number does not reach 20000, which proves that volatile does not guarantee atomic operation

Solution

  1. It must be solved to lock the synchronized keyword on the addNum() method, but the synchronized locking is too heavy, which seriously reduces efficiency
  2. Using the AtomicInteger class
package com.raicho.mianshi.myvolatile;

import java.util.concurrent.atomic.AtomicInteger;

public class VolatileAtomicity {
    volatile int number = 0;

    public void addNum(){
        number++;
    }

    AtomicInteger atomicInteger = new AtomicInteger();

    public void addNumAtomicInteger(){
        atomicInteger.getAndIncrement();
    }

    public static void main(String[] args) {
        VolatileAtomicity va = new VolatileAtomicity();

        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    //va.addNum();
                    va.addNumAtomicInteger();
                }
            },String.valueOf(i)).start();
        }

        //Wait for 20 threads to finish
        while (Thread.activeCount() >2){
            Thread.yield();
        }

//        System.out.println(Thread.currentThread().getName()+" number = "+va.number);
        System.out.println(Thread.currentThread().getName()+" number = "+va.atomicInteger);
    }
}

Disallow instruction reordering

public void mySort(){
    int x=11;//Statement 1
    int y=12;//Statement 2
    x=x+5;//Statement 3
    y=x*x;//Statement 4
}

May change to
1234
2134
1324
Question:
Can statement 4 be rearranged into the first barcode?
There's no way to be the first with data dependency

Using double detection mechanism in single case mode

public class SingletonDemo {

    private static volatile SingletonDemo instance=null;
    private SingletonDemo(){
       System.out.println(Thread.currentThread().getName()+"\t Construction method");
   }

    /**
    * Double detection mechanism
     * @return
     */
    public static SingletonDemo getInstance(){
        if(instance==null){
            synchronized (SingletonDemo.class){
                if(instance==null){
                    instance=new SingletonDemo();
               }
           }
       }
        return instance;
   }

    public static void main(String[] args) {
        for (int i = 1; i <=10; i++) {
            new Thread(() ->{
               SingletonDemo.getInstance();
           },String.valueOf(i)).start();
       }
   }
}
 

In multithreading, instruction rearrangement may occur without volatile keyword, which is thread unsafe

Posted by hhisc383 on Sat, 23 May 2020 20:51:30 -0700