zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    飞腾2000+上面银河麒麟v10 安装virt-manager创建虚拟机的操作过程
    postgresql重置序列起始值
    行为链分析zipkin
    Elasticsearch冷热分离原理和实践
    Window 各个版本对应的版本号大全
    为何fdisk和df 输出的信息不一致?
    Java使用ConcurrentHashMap实现简单的内存式缓存
    Linux系统中如何查找大文件或文件夹的方法
    Class.newInstance()与Constructor.newInstance()创建对象
    Wazuh完整性监测和命令监测注册表并邮件告警
  • 原文地址:https://www.cnblogs.com/volvane/p/9377314.html
Copyright © 2011-2022 走看看