没有新消息
更多内容
0 条评论
暂无评论,快来写下您的评论
问题来自于
林先生
#Java面试题库#请讲一讲synchronized和volatile区别
14150
阅读
2
回答
@2024 职Q 智联招聘
合作商务邮箱:sbyh@zhaopin.com.cn
友情链接
HR圈内招聘/ 同道问答/ 人资知识社区
51社保/ X职场/ HR Bar/ 中人网/ 研招网
京ICP备17067871号 合字B2-20210134
京公网安备 11010502030147号
人力资源许可证:1101052003273号
网上有害信息举报专区
违法不良信息举报电话:400-885-9898
关爱未成年举报热线:400-885-9898-7
朝阳区人力资源与社会保障局 监督电话: 57596212,65090445
#Java面试题库#请讲一讲synchronized和volatile区别
这里我写了一个差不多的代码方便大家理解阅读: public class VolatileUse { public static void main(String[] args) { Flag flag = new Flag(); new Thread(flag).start(); while (true) { //synchronized (flag) { // if (flag.getFlag()) { // System.out.println("gameover"); // break; // } // } if (flag.getFlag()) { System.out.println("gameover"); break; } } } } class Flag implements Runnable { private boolean flag; // private volatile boolean flag; public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } flag = true; System.out.println("flag=" + flag); } public Boolean getFlag() { return flag; } } 大家运行上面的程序,然后区别一下有volatile和没有volatile的区别。 然后我来简单解释一下上面的程序:当没有volatile的时候,我们可以设想三个内存区域:主存,main线程,flag线程。刚开始所有的线程都是flag为false(默认值),main线程开始运行,开了一个flag线程,flag线程里延时两秒后把flag设置为true,此时在flag线程内,flag值就是true,当把flag的值打印出来时,主存的flag的值也变为true,但是main线程的while(true)调用系统比较底层的代码,它的执行效率非常的高,以至于main线程没有机会再一次从主存中获取flag的内存数据,所以main线程里面的flag依然为false。这就出现了内存的可见性的问题。原因就是两个线程在操作共享数据时,对共享数据的操作彼此都是不可见的。 然后大家可以再试试有synchronized加锁的这部分代码,结果和volatile的结果是一致的,也就是说,加锁后,也可以保证内存的刷新。但是,当多个线程去同时去竞争者一把锁的时候,那么就会导致效率极其低下,就有了线程的阻塞,同时就有了大量线程的状态的切换,这是非常消耗cpu资源的。 我这里来补充一下关于volatile的理论上的理解。如果一个字段被声明成volatile,那么Java线程内存模型确保所有线程看到这个变量的值是一致的。一般而言,为了提高处理速度,处理器不直接和内存进行通信,而是先将数据读到内部缓存后再进行操作,但操作完后不知道何时会写到内存中(可以这样理解这个内存模型:每个线程都有自己的内存,然后这个线程内存会时不时地与主内存相互交换数据,建议去看看Java内存模型)。如果对声明了volatile的变量进行写操作,jmm(java内存模型)就会将这个变量所在的缓存行的数据写会到系统内存。当读一个volatile变量时,jmm会把该线程对应的本地内存置为无效,线程接下来将从主内存中读取共享变量。然后有个缓存一致性协议来保证多处理器在操作这个数据时会先去看看和主存的是不是一样。总而言之,就是对这个变量的修改大家都看得见。处理器牺牲了一点效率,换来了线程安全。 关于synchronized的作用 它的几个用法都讲的很清楚了,我也来稍微补充一下:对于synchronized,如果是普通同步方法,那么锁是当前实例对象。如果是静态同步方法,锁是当前类的Class对象。对于同步代码块,锁是synchronized括号里的对象。(如果不太懂的,欢迎参考我前面写的关于synchronized修饰普通同步方法和静态同步方法的区别的文章,可以帮助理解) 然后我们来看看他们两个的区别:volatile是轻量级的同步策略,volatile通常修饰的是字段,synchronized通常修饰的是方法或代码块。从原理上来讲:就是volatile是告诉CPU对它进行读写时要特殊对待,保证内存的可见性,而synchronized是配合上锁的机制保证一段代码在某一时刻只能被一个线程执行完先。即:volatile不具备”互斥性“,而且不具备“原子性”。而synchronized块既保证了块内变量的内存可见性,又保证了代码操作的原子性。这就相当于volatile加上CAS来保证原子性了,但这没有锁的互斥性。