1.共享变量可见性问题
- java内存模型
- 当一个线程操作了共享变量之后,仅仅会写入该线程缓存中,并不会及时写入主存中,那么别的线程此时从主存中获取到共享变量,就会导致异常。
-
volatile关键字:如果一个变量声明为volatile后,每个线程操作共享变量后就会立即同步到主存中,此功能是底层硬件支持的。也就是说volatile关键字解决了共享变量的可见性问题,但没有解决变量的原子性以及互斥性。 原子类中的变量全部声明为volatile,保证线程可见性,原子类使用cas算法保证原子性。
public class VolatileDemo { public static void main(String[] args) { ThreadClass threadClass = new ThreadClass(); Thread thread = new Thread(threadClass); thread.start(); while (true){ if (threadClass.shareVar){//在这里main线程访问共享变量,该共享变量是main线程的变量副本 System.out.println("ShareVariableClass.shareVar:" + threadClass.shareVar); break; } } } } class ThreadClass implements Runnable{ public volatile boolean shareVar = false; @Override public void run() { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } this.shareVar = true; System.out.println("sharVar change to " + shareVar); } }
结果如下,结果中打印的顺序会不一致。
2.CAS算法
CAS:全称compare and swap,比较并交换。当多个线程操作共享变量的时候,每个线程的操作都写入主存中,就会导致主存中的共享变量不能够保证原子性。如果要操作一个变量s进行+1操作,在+操作之前,获取主存中s的值(通过volatile关键字保证可见性)作为期望值expectValue,将s进行+1操作后,写入主存之前通过将目前的主存值和expectValue值做比较,如果相同则将s+1的值写入主存,如果不相等,则重复进行上述步骤。
以AtomicInteger举例
AtomicInteger atomicInteger = new AtomicInteger(10);
int andIncrement = atomicInteger.getAndIncrement();
cas操作最终调用的方法是Unsafe类中的native方法调用,除了Integer类型的原子操作,还有Boolean等的类型操作,但调用是如下三个方法来保证原子性。boolean类型是通过转换为int类型来进行操作的。
所有原子类如下
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。