首先明确和synchronized的区别:
1:volatile是变量修饰符,而 synchronized作用于一段代码或者是方法。
2:多线程访问volatile不会发生阻塞,而synchronized会出现阻塞。
3:volatile能保证数据的可见性,但是保证不了原子性,而synchronized可以保证原子性,也可以间接地保证可见性,因为他会将私有内存和公共内存中的数据做同步。
4:关键字volatile是解决变量多个线程之间的可见性,关键字synchronized是解决多个线程之间访问资源的同步性。
没有使用关键字volatile
package cn.itcast.java.base.isvolatile;
public class MyThread extends Thread
{
public static int count = 0;
public static void inc() {
//这里延迟1毫秒,使得结果明显
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
count++;
}
public static void main(String[] args) {
//同时启动1000个线程,去进行i++计算,看看实际结果
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
MyThread.inc();
}
}).start();
}
//这里每次运行的值都有可能不同,可能为1000
System.out.println("运行结果:Counter.count=" + MyThread.count);
}
}
我们来看一下运行结果:
运行结果:Counter.count=979
首先我来说一下count++这个语句,该语句进行的操作是将值从内存的地址读取到寄存器中,对寄存器的值进行加1操作,然后再把新值写回到内存中,由于count++并不是原子的,所以俩个线程或者是多个线程同时进行++就会产生数据混乱,也就是各个线程的count是不一致的。
当多个线程对count变量进行拷贝的时候,由于在各个线程中count的值是不一样的,比如一个线程A将count改变成了80,存放在主内存中是80,而另一个线程B的此时正在运行的count值却是在修改80之前拿的,也就是79,于是它又加了一个1,变成了80。线程B改变了各自的值,但是这个改变是还没有来得及传递给主内存区域或者其他线程时候就已经发生的了。
使用关键字volatile
package cn.itcast.java.base.isvolatile;
public class MyThread extends Thread
{
public volatile static int count = 0;
public static void inc() {
//这里延迟1毫秒,使得结果明显
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
count++;
}
public static void main(String[] args) {
//同时启动1000个线程,去进行i++计算,看看实际结果
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
MyThread.inc();
}
}).start();
}
//这里每次运行的值都有可能不同,可能为1000
System.out.println("运行结果:Counter.count=" + MyThread.count);
}
}
运行结果:
运行结果:Counter.count=791
为什么使用了,却还不是1000呢?因为它保证可见性,却不能保证原子性。
关键字volatile是可以知道变量的值被更改了,并且可以获的最新的值的使用,也就是用多线程读取共享变量时候可以获得最新值使用。
为了保证可见性和原子性,我们可以一起用关键字volatile和synchronized。
线程工作的内存
在多线程环境中,use和assign是多次出现的,但是这个并不能保证原子性,也就是在read和load之后,如果主内存变量count发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,也就是私有内存和公共内存中的变量不能同步,所以计算出来的结果和预期不一样,也就出现了非线程安全的问题。
对于用volatile修饰的变量,JVM虚拟机只是保证从主内存加载到线程工作内存的值是最新的,例如线程1和线程2都进行了load和read操作,发现主内存中count的值都是5,那么他们都会加载这个最新的值,也就是volatile解决的是可见性问题。