前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >为什么volatile使用比synchronized少

为什么volatile使用比synchronized少

作者头像
java达人
发布2018-01-31 18:19:28
4820
发布2018-01-31 18:19:28
举报
文章被收录于专栏:java达人java达人

在多线程编程中,我们最常用的是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进行代替和优化,但前提是你必须充分理解其使用场景和涵义。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2016-06-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 java达人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档