1.volatile
volatile修饰的成员变量,强制要求当前线程同步堆内存里的对应成员变量;堆内存数据所有线程共享,每个线程都有自己独立的线程栈(栈内存);堆内存的成员变量的值会同步给进来的各个线程栈,其中一个线程如果需要修改堆内存中的成员变量,首先修改栈内存中的成员变量又称变量副本,然后同步到堆内存里对应成员变量,由于线程栈都是相互独立的,堆内存的对应已经修改成员变量未及时同步其他线程栈,导致其他线程未获取最新数据,volatile关键字会强制当前线程的线程栈里的变量副本同步堆内存里对应的成员变量,保证数据同步
一、无volatile关键字修饰代码demo
代码demo程序进入t1线程里while死循环;
原因:Money.money是t1线程和t2线程的共享数据,t1线程最先开始运行时满足while条件会进入循环,当t2线程开启时已经修改了共享数据,但是t1线程栈中的while条件变量副本还是旧数据并且未同步最新共享数据导致t1线程while条件一直满足程序无法结束,进入死循环二、volatile关键字修饰代码demo
代码demo里t1线程已经跳出while死循环;
原因:Money.money是t1线程和t2线程的共享数据,使用了volatile关键字修饰成员变量,强制当前线程执行任务时,把线程栈里的变量副本同步共享数据(堆内存)最新值,才能继续执行线程任务;最后t1线程当while循环不满足条件跳出死循环,执行循环外面代码,线程任务结束,程序结束三、volatile关键字不能保证数据的原子性
多线程环境中的线程栈(栈内存)变量副本无法及时同步到共享数据(堆内存)里,导致无法保证成员变量数据值的原子性;
原因:此时当前线程只修改线程栈中的变量副本未及时同步至共享数据(堆内存),就已经失去CPU执行权;另一个线程获取到CPU执行权,获取共享数据时还是第一个线程未及时同步旧的共享数据
内存模型
volatile代码demo
以上demo中一个线程执行打印100次,for循环100次创建100个线程,所以最终结果是总共打印10000次;但最终结果是9997次
原因:多线程环境中,每个线程获取CPU执行权都是随机的,即使共享数据count成员变量(atom线程任务对象只实例化了一次,后面代码才开始多线程执行,因此成员变量count是后面多线程执行任务的共享数据)加了volatile关键字可以同步共享数据中的最新值,但其他线程栈中已经修改的变量副本还没同步到共享数据时,已经失去了CPU的执行权,获取CPU执行权的线程同步的共享数据还是旧的,导致最终结果错误
解决办法:共享数据代码加锁,synchronized同步代码块可以确保当前线程任务执行完成才会释放锁,其他线程即使获取了CPU执行权却没有获取锁对象也只能在外面等待
备注:锁和CPU执行权可以确保当前线程完成线程任务并且不被其他线程干扰;锁:不被其他线程干扰,CPU执行权:执行线程任务四、synchronized同步代码块解决volatile关键字无法实现原子性问题
2.synchronized同步代码块
synchronized作用和volatile关键字一样保证副本变量和堆内存成员变量同步
synchronized同步代码块Demo