Java多线程之内存可见性和原子性操作 一 synchronized

可见性的理论

 

 

就说这个线程是可见的

 

 

 什么是线程的工作内存

工作内存是java内存模型提出的概念

JMM

变量是指共享变量

所有的变量都存储在主内存

每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝)

变量的源保存在哪里呢:主内存

 

工作内存和主内存的关系

下面的X就是三个线程的共享变量

 

 

 

 

 

 共享变量可见性的原理

 

 

共享变量可见性的原理

 

 

要想使得线程2能及时更新X的值,则需要工作内存1的值及时刷新到主内存,然后工作内存2的值从主内存中读取出来。

 

 

 两个步骤其中任何一个步骤出了差错,都会导致变量不可见。会导致数据的不准确,从而是的线程不安全。所以在编写代码的时候要保证共享变量的可见性

 


关于可见性 

满足两点可以保证可见性

 

这里指语言层面,所以不包括concurrent并发包下的高级特性

可以实现互斥锁(原子性),用synchronized

但是他也有另个功能,即实现内存的可见性

Synchronized实现可见性

这样就可以保证共享变量的变化,在其他线程枷锁前,对其他线程可见(也就是其他线程能及时更新共享变量的变化)

这六个步骤可以结合刚刚的两条规定来理解。

 

指令重排序

重排序的概念

不理解也没关系,可以看个下面简单的例子,代码顺序,和实际执行顺序不一致,就是重排序的一种体现

看的懂上面的意思即可,有可能执行顺序和代码顺序不一样

as-if-serial

 

 

Java在单线程下一定遵循这个条件。

 

举个例子:

1 2行重排序,但是第三行不能重排序(不能1 2行之前执行)。多线程中可能会有问题

 一个例子

package mkw.demo.syn;

public class SynchronizedDemo {
    //共享变量
    private boolean ready = false;
    private int result = 0;
    private int number = 1;   
    //写操作
    public void write(){
        ready = true;                           //1.1                
        number = 2;                            //1.2                
    }
    //读操作
    public void read(){                    
        if(ready){                             //2.1
            result = number*3;         //2.2
        }       
        System.out.println("result的值为:" + result);
    }

    //内部线程类
    private class ReadWriteThread extends Thread {
        //根据构造方法中传入的flag参数,确定线程执行读操作还是写操作
        private boolean flag;
        public ReadWriteThread(boolean flag){
            this.flag = flag;
        }
        @Override                                                                    
        public void run() {
            if(flag){
                //构造方法中传入true,执行写操作
                write();
            }else{
                //构造方法中传入false,执行读操作
                read();
            }
        }
    }

    public static void main(String[] args)  {
        SynchronizedDemo synDemo = new SynchronizedDemo();
        //启动线程执行写操作
        synDemo.new ReadWriteThread(true).start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //启动线程执行读操作
        synDemo.new ReadWriteThread(false).start();
    }
}

 第一种情况

 

假设在1.1后让出了cpu资源,那么这时候执行2.1,也就是读线程开始执行,这时候ready是true,执行2.2 result变成3.

输出结果是3。1.2可能在输出后再执行,但这就导致输出的时候result是3

 

 第二种情况

先执行1.2,说明进行了指令重排序,打印了初始值 0

 还可以2.1和2.2重排序,他们重排序后的结果是这样的

中间加了一个mid变量保存中间结果

 只有数据依赖关系才会禁止指令重排序。

可见性分析

导致共享变量在线程间不可见的原因

 

 synchronize实现可见性的方法

即共享变量在某个线程被改变能及时在其他线程使用之前更新

 

 

上面的方式就可以保证读写线程不会交叉执行

 

Java多线程之内存可见性和原子性操作 二 volatile

 

posted @ 2018-08-03 13:27  Volvane  阅读(2169)  评论(0编辑  收藏  举报
回顶部
//回顶部