java synchronized与volatile的区别

java线程同步有两个特性,一个是可见性,一个是有序性。在解释这两个概念之前,先说下两个重要的概念,主内存(main memory)和工作内存(working memory),线

程之间数据的交互不是直接传递,而是通过共享变量来实现的。对象的创建是在主内存中,而线程用到该对象时,是先拷贝一个该对象的副本放到线程的工作内存中,然后对该副

本对象进行操作,完成后在刷新到主内存中。而这个操作过程中的几个步骤不是原子性的,这样就有可能造成读脏数据等问题。关于这个问题,网上有这段解释:

根据java语言规范的规定,线程的工作内存和主存间的数据交换是松耦合的,什么时候需要刷新工作内存或者什么时候更新主存的内容,可以由具体的虚拟机实现自行决定。由于

JVM可以对特征代码进行调优,也就改变了某些运行步骤的次序的颠倒,那么每次线程调用变量时是直接取自己的工作存储器中的值还是先从主存储器复制再取是没有保证的,任

何一种情况都可能发生。同样的,线程改变变量的值之后,是否马上写回到主存储器上也是不可保证的,也许马上写,也许过一段时间再写。那么,在多线程的应用场景下就会出

现问题了,多个线程同时访问同一个代码块,很有可能某个线程已经改变了某变量的值,当然现在的改变仅仅是局限于工作内存中的改变,此时JVM并不能保证将改变后的值立马

写到主内存中去,也就意味着有可能其他线程不能立马得到改变后的值,依然在旧的变量上进行各种操作和运算,最终导致不可预料的结果。

现在来解释下可见性和有序性。可见性是指对于一个共享变量,当一个线程改变了其值时,这个变化应该对另一个线程可见,即另一个线程读到的值就是改变后的值。而有序

性是指对于共享变量的访问,当有一个线程在访问该变量时,其他线程必须等到该线程访问结束后才能去访问。这样就保证了同一时刻只有一个线程在访问,修改共享变量。

synchronized可以修饰方法,代码块,实际上都是获取了一个对象作为锁,在线程进入synchronized块之前,会把工作存内存中的所有内容映射到主内存上,然后把工作内存

清空再从主存储器上拷贝最新的值。而在线程退出synchronized块时,同样会把工作内存中的值映射到主内存,不过此时并不会清空工作内存。这样一来就可以强制其按照上面的

顺序运行,以保证线程在执行完代码块后,工作内存中的值和主内存中的值是一致的,保证了数据的可见性,并且当有其他线程试图访问synchronized的方法时,也必须要获取锁

对象,但是由于锁被当前线程所占用,只能等待该线程释放锁对象,也就保证了有序性。

volatile修饰变量,只保证了可见性,没有保证有序性。当线程访问被volatile修饰的共享变量时,会直接从主存中读取,修改也是直接修改主存中的数据,这样,由于缺少拷贝

的过程,其他线程访问该共享变量时,肯定是修改后的数据,即保证了可见性。但是如果多个线程同时访问了该共享变量,并做了修改,这样就容易产生有序性问题。如:

count=100,线程a 读取后加10, 线程b读取后-10,a,b可能同时读到了100,做修改后可能count值为90或110,这样就出现了脏数据的问题。故对volatile的使用,需要谨慎。

原文地址:https://www.cnblogs.com/marcotan/p/4256906.html