内存模型
概念解释
JMM:多线程操作下的一系列规范约束,同时屏蔽了不同硬件和操作系统内存的访问差异
主内存:即JVM堆内存
工作内存:cpu的寄存器和高速缓存的抽象描述。
缓存一致性协议:主内存与工作内存数据交互的一系列协议,read、load、assign、use、store、write、lock、unlock。lock和unlock,直接对应着synchronized关键字的monitorenter和monitorexit字节码指令。
原子性:表示某个操作或某些操作是原子的,不可分隔的。
可见性:变量改变可以立即被其他线程感知到。
多线程并发问题
因为线程工作内存会缓存从主内存读取的变量,所以其他线程不能及时读取变量最新值,导致多线程对同一共享变量同时进行操作时的数据不一致问题。工作内存与主内存的数据读取与写入是由多个原子指令组成,致使对共享变量的读取与写入整体是非原子性的。
解决多线程并发问题
Synchronized和Lock锁。锁的具体使用请参考其他文章。
Volatile
具有内存可见性,但并不能解决线程安全问题。Volatile只能保证线程每次访问变量时都从主内存获取,但因为主内存与工作内存数据交互是非原子的,所能不能保证线程安全。
示例
不加任何并发处理
初始化共享变量x = 0,线程A访问共享变量x加1,此时因为第一次访问线程A的工作内存中没有变量x所以从主内存中加载,修改x为1后同时写回主内存,此时线程B执行x加1操作,因为工作内存中没有变量x也从主内存读取,加1后写回主内存,此时主内存中x变量的值为2,线程A又对x进行加1操作,因为此时线程A的工作内存中已经有变量x且值为1,并不会去主内存中重新读取x的值,此时对变量x加1后值为2并写回主内存,主内存x变量的值为2。总共对变量x进行了3次加1操作,但主内存中的值为2,所以出现了并发不一致问题。
使用volatile修饰变量x
当线程A第二次对变量x进行加1操作时,线程A并不会从自己的工作内存中读取变量x而是强制从主内存中读取当前变量x的值,所以此时读取的变量x的值为2,加1后写回主内存,主内存变量x的值应该为3。但为什么volatile是非线程安全的呢?因为工作内存与主内存的读取和写入是非原子的,有可能线程B加1操作还未完成写回主内存时,线程A执行了加1操作,这时线程A读取的变量x的值为1,加1操作写回主内存后,主内存变量x的值任然为2,还是出现了不一致问题。
加synchronized或lock锁
线程A或线程B对变量x执行加1操作时,先判断是否有其他线程已经在操作变量x,如果有其他线程在操作则当前操作阻塞直到其他线程执行完成并将变量x的值写回主内存后,才允许当前的加1操作。