JMM 用来定义程序中变量的访问规则, 定义者, 想要屏蔽掉不同的硬件和系统造成的内存访问差异.
之前了解的 JMM 空也曾提到工作内存的概念, 每个线程都有自己的工作内存, 所有的变量多存在主内存, 工作内存存储的是各个线程用到的变量 主内存的副本拷贝, 工作内存之间不能直接操作对方工作内存的变量, 要通过主内存作为中间介, 各个线程只能操作各自的工作线程变量, 无法直接操作主内存变量.
然后就是就是定义了 8 中原子操作, 用来控制变量, 分别是 lock,unlock,read,load,use,assign,store,write.
定义了 8 个规则
1 一个变量只能被一个线程 lock, 能被同一个线程多次 lock, 相应的要多次 unlock, 后面说的就是可重入.
2lock 一个变量后, 清除自己的工作内存先, 再要使用时, 重新从主内存加载; unlock 后, 要同步到主内存.
3 变量只能定义在主内存, 在使用 user 或者 store 前要 load 或者 assign
4 工作内存的变量没变化, 不能平白无故同步主内存, 一旦改变一定要同步主内存.
以上是基础知识. 摘自《深入了解 java 虚拟机》
再遇到两个线程同时操作一个对象的字段时, 遇到了一些问题,
先贴代码
- public class MyObject {
- private String name="1";
- private String pass="11";
- public void print() {
- System.out.println(name+" "+pass);
- }
- public void setvalue(String u,String p){
- this.name=u;
- if(Thread.currentThread().getName().equals("a")){
- System.out.println("a 停止");
- Thread.currentThread().suspend();
- }
- this.pass=p;
- }
- }
- public class Test {
- public static void main(String[] args) throws Exception{
- final MyObject object = new MyObject();
- Thread thread2 = new Thread(){
- @Override
- public void run() {
- while (true){
- object.print();
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- };
- thread2.start();
- Thread thread1 = new Thread(){
- @Override
- public void run() {
- object.setvalue("a","aa");
- }
- };
- thread1.setName("a");
- thread1.start();
- Thread.sleep(500);
- //thread2.start();
- }
描述: thread2 和 Thread1 会同时去操作一个 object 对象,
thread2 运行后, 输出的 是 1 和 11, 在 Thread1 启动后, 会改变 object 的字段值, 这时候 thread2 循环输出的也变了, 变成了 a 和 11.
思考:
1 每个线程都有自己的工作内存, 会把 object 对象 (字段为 1,11) 拷贝一份到 thread2 和 Thread1 的各自工作内存, 第一次 thread2 输出字段为 (1,11) 可以理解.
2 但是当 Thread1 启动后改变了期工作内存的 object 字段变成(a,11),
3 接下来 thread2 输出字段为也变成了 (a,11)
于是接在思考不是有各自的工作内存吗, 线程 thread1 也没有停止啊, 为什么线程 thread2 的工作内存变量也会变掉.
以下是自己的看法
线程 thread1 的 suspend()方法会同步到主内存, 还有 sleep()也是如此.
然后接下来就是怎么 主内存同步到 线程 thread2 的工作内存中
这是别人见解
为了提升性能, 线程里面有工作内存, 这样访问数据不用去主存读取, 可以快一些. 共享变量被线程修改后, 该线程的工作内存中的值就会和其他线程不一致, 也和主存的值不一致, 所以需要将工作内存的值刷入主存, 但是这个刷入可能其他线程并没有看到.
使用 volatile 后可以通过 CPU 指令屏障强制要求读操作发生在写操作之后, 并且其他线程在读取该共享变量时, 需要先清理自己的工作内存的该值, 转而重新从主存读取, volatile 保证一定会刷新, 但是不写也不一定其他线程看不见.
就是不一定, 有随机性, 不加 voliatile 其他线程 也不一定看不见. 加了一定看的见.
这就是目前我的理解.
来源: http://www.bubuko.com/infodetail-3198422.html