都知道volatile可以保证多线程对一个对象的可见性,对于volatile修饰的对象或者一个变量,线程去读取的时候每次都会到主内存去读取,对变量的更新也会flush到主内存。这就保证了,这个变量对于每个线程都是一致的,不会存在由于cpu缓存存在而导致的不一致的情况。用了一个demo来帮助自己理解。
//对象
public class Student {
public volatile int age = 0;
}
//线程类
public class TestThreadA implements Runnable{
Student student1 ;
public TestThreadA(Student student) {
student1= student;
}
@Override
public void run() {
student1.age++;//对age+1操作
System.out.println(Thread.currentThread().getName()+"*****"+student1.age);
}
}
//启动类
public class Main {
public static void main(String[] args) throws InterruptedException {
Student student = new Student();
for(int i = 0;i<50;++i){
Thread aThread = new Thread(new TestThreadA(student));
aThread.setName("Thread*******"+i);
aThread.start();
}
System.out.println(student.age);
}
}
在Student类变量age未加volatile修饰时:
50个线程对age=0进行+1操作,预期的结果应该是50,由于不同线程栈中age缓存的不一致,导致最终结果不正确。当给Student类中age加volatile修饰时:
运行多次发现,结果依然会有不为50的出现.
原来volatile只保证可见性但不保证原子性,Volatile实现内存可见性是通过store和load指令完成的;也就是对volatile变量执行写操作时,会在写操作后加入一条store指令,即强迫线程将最新的值刷新到主内存中;而在读操作时,会加入一条load指令,即强迫从主内存中读入变量的值。例如age++时,线程1先从主内存读取age=1,此时让出了CPU执行权,线程2获得执行执行,也读取到age=1,继续age++,然后写入主内存,主内存中age=2,此时线程1重新获得了执行,age++,结果为2,写入主内存,主内存中结果仍然为2。所以一般在多线程中使用volatile变量,为了安全,对变量的写入操作不能依赖当前变量的值:如age++或者age=age*2这些操作。
那么synchornized呢?
synchornized既保证可见性又保证原子性。
对Student类变量age去掉volatile修饰。TestThreadA类中的student1.age++用synchornized:
synchronized (student1) {
student1.age++;
}
结果:
此时结果正确
synchronized会对对象加锁,同一时刻只有一个线程能访问该对象,也就是synchornized修饰的代码块,只能一个一个的线程同步执行,不能并发执行。那么volatile就不会造成线程的阻塞;而synchronized可能会造成线程的阻塞,也可能会造成死锁。
查看运行结果可以发现,main线程总是早于其他线程先结束,最终结果并不是程序的最终结果50。
join()方法就可以保证程序的最终结果50。a.join();就是让当前线程将cpu执行权交给a线程,直到a运行结束。
TestThreadA类中的student1.age++中的synchronized修饰去除。
Main类改为:
public class Main {
public static void main(String[] args) throws InterruptedException {
Student student = new Student();
for(int i = 0;i<50;++i){
Thread aThread = new Thread(new TestThreadA(student));
aThread.setName("Thread*******"+i);
aThread.start();
aThread.join();
}
System.out.println(student.age);
}
}
此时运行结果:
相当于一个一个线程顺序执行