static和volatile的原理及区别,看这篇就够了

static


表示变量在方法区中,所有线程共享。
JVM内存结构,把内存分为 堆、栈、方法区、本地方法栈和程序计数器。其中堆和方法区被所有线程共享,在内存模型中被成为主内存,而栈、程序计数器属于线程私有,在内存模型中被成为工作内存。

总结
JVM内存= 堆+栈+方法区+本地方法栈+程序计数器       
主内存 = 堆+方法区             (所有线程共享)
工作内存 = 栈+程序计数器   (线程私有)

前面说到static在方法区中,那有两个线程,一个去写,一个去读,是不是就能马上读到最新的值呢,未必!
这里要说下,JVM规范定义了线程对内存间交互操作:
Lock(锁定):作用于主内存中的变量,把一个变量标识为一条线程独占的状态。
Read(读取):作用于主内存中的变量,把一个变量的值从主内存传输到线程的工作内存中。
Load(加载):作用于工作内存中的变量,把read操作从主内存中得到的变量的值放入工作内存的变量副本中。
Use(使用):作用于工作内存中的变量,把工作内存中一个变量的值传递给执行引擎。
Assign(赋值):作用于工作内存中的变量,把一个从执行引擎接收到的值赋值给工作内存中的变量。
Store(存储):作用于工作内存中的变量,把工作内存中的一个变量的值传送到主内存中。
Write(写入):作用于主内存中的变量,把store操作从工作内存中得到的变量的值放入主内存的变量中。
Unlock(解锁):作用于主内存中的变量,把一个处于锁定状态的变量释放出来,之后可被其它线程锁定。

上面一段,只要知道,工作内存中保留了一份主存中的变量副本,因此写操作未必能马上更新到主存,读操作也未必能马上读取到主存中更新后的值,这与cpu时间片有关,能不能读取最新的值看缘分。

总结

static 解决所有实例共享数据的问题,不能解决多线程条件下主存和工作内存的同步性问题。说白了就是一个线程更新了值,另一个线程未必能马上取到最新的值。因此volatile发挥了作用。

volatile

保证可见性,不保证原子性

volatile保证了每次工作内存操作前都去主存中取最新的值,解决了内存不可见性的问题,这是static没法做到的。
缺陷 不保证原子性
实例:
  比如两个线程对volatile变量进行i++操作1000次,实际跑出来的i值会<=2000,为什么?
  一个线程操作完工作内存里的副本变量,会更新到主存中,在更新主存的过程中会进行加锁操作lock,此时如果另一个线程也想更新主存中的同一变量,就会自动失效本次操作,等上一线程更新完,再重新读取主存中的值。
  因此,会有失效多次i++的情况,值就小于等于2000。

static和volatile的区别

相同点:

1.他们都能够达到所有线程共享的目的。

2. 对于非原子性操作i++ 都是线程不安全的,可能出现数据重复。
不同点:

1.static 不保证可见性,未必能取到主存中最新的值。

2.volatile保证可见性,保证取到主存中最新的值。

volatile应用场景

说到volatile不得不提,他的应用场景,static随处可见,volatile真的很少用到。

1.计数器不能用,数据可能重复,导致脏读。  即i++操作

2.状态标志可以用。

一个线程改boolean标志,另一个线程使用这个boolean变量。使用volatile可以保证马上能取到最新的值,每次去主存取。

其他场景就大同小异,主要记住这两点。


 

猜你喜欢

转载自blog.csdn.net/x18094/article/details/108429511