线程安全问题
线程安全是线程同步问题。
图中是一个非常典型的读写不同步的问题,线程A的写操作没有被B及时看见,所以B的读写数据均为异常的。
线程安全问题最常见的解决方案就是给共享变量加synchronized锁。
内存可见性问题
java内存模型如图所示。当线程工作时,将主线程中的变量复制到自己的工作内存中,线程读写实际是操作自己工作内存中的变量。
实际工作时线程的工作内存:
图片展示的是一个双核CPU架构,每个核都有自己的一级缓存,有点有二级缓存。
内存可见性问题的出现:
1、当A读取数据时,回先在缓存中查找,若缓存中没有对应的数据则到主内存中查找并将数据保存到缓存中。
2、当B对同一个共享变量进行写操作时,会将数据加载到自己的缓存中,并进行写操作,同时将新的值写到主内存中。
3、A再对同一个数据进行写或读操作时,会在缓存中命中,不会再从主内存中查找,因此B对数据的修改对于A来说是不可见的。
解决共享变量不可见问题的方法是使用volatile关键字。
synchronize
synchronize关键字可以获取共享变量的监视器锁。synchronize的内存语义是把synchronize块内用到的变量从线程的工作内存中清除,这样使用变量时就不会在工作内存中获取,而会从主内存中获取,因此synchronize能解决内存可见性问题。另外,synchronize是一种原子性的锁,所以能用于实现原子性操作。
synchronize的使用会导致上下文切换,这会带来线程调度的开销。
volatile
当一个变量被volatile修饰时,对共享变量的读写将会在主内存中进行,保证了内存可见性。另外,volatile并不能保证操作的原子性,不能解决前面提到的线程同步的安全问题。
使用volatile关键字的情形:
1、写入变量不依赖它的当前值时。
2、读写变量没有加锁。