Testing volatile is not atomic. Should volatile be added sysnchronized? Do you need to add volatile if you add sysnchronized?

Do you use sysnchronized to add volatile?

/**
 * 测试volatile不具有原子性
 */
public class T {
    
    
    volatile int count=0;  //属性上加volatile 保证可见性

    public void sumCount(){
    
     
        for (int i = 0; i <1000 ; i++) {
    
    
            count++;  //count++操作在底层是好几步来实现,它本身不是一个原子性的操作
        }

    }

    public static void main(String[] args) {
    
    
        T t=new T();

        List<Thread> threads=new ArrayList<>();


        for (int i = 0; i <10 ; i++) {
    
    
         threads.add(new Thread(() ->{
    
    
             t.sumCount();
            }));
        }

        threads.forEach((o)->{
    
    
            o.start();
        });

        threads.forEach((o)->{
    
    
            try {
    
    
                o.join();  //保证main线程到当前线程执行完成后再执行
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        });

        System.out.println(t.count);


        //理论上应该得到的值是10000,在加volatile的情况下,虽然它能保证可见性,但是不能保证原子性,
        //所以的到的结果不对,在方法上加synchronized来保证原子性就安全了




    }


}

In theory, the value that should be obtained is 10000. In the case of adding volatile, although it can guarantee visibility, it cannot guarantee atomicity, so it is also unsafe.
In order to ensure safety, you need to add synchronized to the method to ensure atomicity

 public synchronized void sumCount(){
    
      //方法上加synchronized保证原子性
        for (int i = 0; i <1000 ; i++) {
    
    
            count++;  //count++操作在底层是好几步来实现,它本身不是一个原子性的操作
        }

    }

The value obtained in this way is 10000, which is safe

Do you need to add volatile if you add sysnchronized?

After the double check, should I add volatile?
The answer is yes. Although this is generally undetectable, the problem of instruction reordering will occur if volatile is not added! When instance=new instanse() this object is compiled by the compiler, the instructions are divided into three steps:
1. Request memory for the instruction
2. Initialize the member variable
3. Assign this memory to instance
If the instruction executes the third step first, Then this object has not been created yet, but another thread comes to see that the instance is not null, it will not go to the locked piece of code, and use this object directly, but this object is a semi-initialized object, in this case, There will be a problem

/**
 * 懒汉模式4 -双重加锁
 *
 */
public class Singleton05 {
    
    
    private volatile static Singleton05 singleton05;  //需要加VOLATILE禁止指令重排序

    private Singleton05() {
    
    
    }

    public static Singleton05 getInstance() throws InterruptedException {
    
    

        if (singleton05 == null) {
    
    
            //加双重判断
            synchronized (Singleton05.class){
    
    
                if(singleton05==null){
    
    
                    Thread.sleep(1);
                    singleton05 = new Singleton05();

                }

            }

        }
        return singleton05;

    }

    public static void main(String[] args) throws InterruptedException {
    
    

        for (int i = 0; i < 100; i++) {
    
    
            new Thread(() -> {
    
    
                try {
    
    
                    System.out.println(Singleton05.getInstance().hashCode());
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }).start();


        }


    }
}

It is troublesome to add volatile and sysnchronized, can there be other ways?
Yes, we can achieve thread safety through cas operations.
For details, see another cas lock-free optimization, spin lock, and Atomic class on my blog
.

Guess you like

Origin blog.csdn.net/yanfei464486/article/details/112511803