为什么volatile使用比synchronized少

在多线程编程中,我们最常用的是synchronized,而对volatile的使用,却相对较少。这一方面是因为volatile的使用场景限制,另一方面是volatile使用需要更高的技术水平。

    

我们先来看一幅java内存模型图:


每一个线程都有相应的工作内存,工作内存中有一份主内存变量的副本,线程对变量的操作都在工作内存中进行(避免再次访问主内存,提高性能),不同线程不能访问彼此的工作内存,而通过将操作后的值刷新到主内存来进行彼此的交互,这就会带来一个变量值对其他线程的可见性问题。当一个任务在工作内存中变量值进行改变,其他任务对此是不可见的,导致每一个线程都有一份不同的变量副本。而volatile恰恰可以解决这个可见性的问题,当变量被volatile修饰,如private volatile int stateFlag = 0; 它将直接通过主内存中被读取或者写入,线程从主内存中加载的值将是最新的。

 

但是volatile的使用有着严格的限制,当对变量的操作依赖于以前值(如i++),或者其值被其他字段的值约束,这个时候volatile是无法实现线程安全的。被volatile修饰的变量必须独立于程序的其他状态。因为volatile只是保证了变量的可见性,并不能保证操作的原子性,所谓原子性,即有“不可分”的意思,如对基本数据类型(java中排除long和double)的赋值操作a=6,如返回操作return a,这些操作都不会被线程调度器中断,同一时刻只有一个线程对它进行操作。

 

看以下代码:

 

public class VolDemo {

 

 private static volatile int counter;

 

 public static void incNum() {

  counter++;

 }

 

 public static void main(String[] args) {

  ExecutorService executor = Executors.newCachedThreadPool();

  for (int i = 0; i < 1000; i++) {

   executor.execute(new Runnable() {

    @Override

    public void run() {

     VolDemo.incNum();

    }

   });

 

  }

 

  executor.shutdown();

  try {

   if (!executor.awaitTermination(1000, TimeUnit.MILLISECONDS)) {

 

    System.out.println("some task are not terminated");

   } else {

    System.out.println("result:" + counter);

   }

  } catch (InterruptedException e) {

   // TODO Auto-generated catch block

   e.printStackTrace();

  }

 

 }

 

}

 

多次运行结果:

result:998

result:988

result:995

......

 

预期结果应该是1000,尽管counter被volatile修饰,保证了可见性,但是counter++并不是一个原子性操作,它被拆分为读取和写入两部分操作,我们需要用synchronized修饰:

 public static synchronized void incNum() {

  counter++;

 }

 

此时每次运行结果都是1000,实现了线程安全。synchronized是一种独占锁,它对一段操作或内存进行加锁,当线程要操作被synchronized修饰的内存或操作时,必须首先获得锁才能进行后续操作;但是在同一时刻只能有一个线程获得相同的一把锁,所以它只允许一个线程进行操作。synchronized同样能够将变量最新值刷新到主内存,当一个变量只被synchronized方法操作时,是没有必要用volatile修饰的,所以我们接着把变量声明修改为:

 

 private static  int counter;

 

多次运行结果依旧是1000。

 

综上所述,由于volatile只能保证变量对多个线程的可见性,但不能保证原子性,它的同步机制是比较脆弱的,它在使用过程中有着诸多限制,对使用者也有更高的要求,相对而言,synchronized锁机制是比较安全的同步机制,有时候出于提高性能的考虑,可以利用volatile对synchronized进行代替和优化,但前提是你必须充分理解其使用场景和涵义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值