volatile修饰全局变量,可以保证线程并发安全吗?

 

今天被人问到volatile能不能保证并发安全?

呵,这能难倒我?

 

直接上代码:

public class ThreadTest {
    // 使用volatile修饰变量
    private static volatile int num = 0;

    public static void main(String[] args) throws InterruptedException {
        // 循环创建10个线程并执行,每个线程内对volatile修饰的num进行递增
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int ii = 0; ii < 1000; ii++) {
                    autoIncrement();
//                    autoIncrementLock();  
//                    System.out.println(num);  这是一个彩蛋,猜猜看sout打印会不会影响结果?
                }
            }).start();
        }
        // 睡眠3秒以等待每个线程内递增结束
        Thread.sleep(3000);
        System.out.println("最终结果为:"+num);
    }

    // 无锁递增
    private static void autoIncrement() {
        num++;
    }

    // 使用synchronized修饰递增方法,给其上锁
    private static synchronized void autoIncrementLock() {
        num++;
    }
}

期望最终值:
  10 * 1000 = 10000
运行无锁autoIncrement()三次,结果分别为:
  第一次:8666
  第二次:7767
  第三次:9084
运行被synchronized修饰的方法三次,结果分别为:
  第一次:10000
  第二次:10000
  第三次:10000

分析:

  10个线程对volatilei修饰的num++,会被编译成以下三步:

      1. 获取num的值;
      2. 执行num+1;
      3. 将结果赋值给num。

  volatile关键字只能保证可见性,并不能保证原子性。

 

结论:
  volatile关键字只能保证这3步在编译后指令不会被重新排序,并不能保证多个线程并发的数据安全。建议在方法上加上synchronized修饰或配合其他Lock锁使用。


posted @ 2021-04-21 20:40  杨飞只是太过正经  阅读(591)  评论(0)    收藏  举报