• 并发编程(原理篇 上)


    一、前言

    七月的天气分外炎热,心中燥意难以抚平,决定梳理下之前落下的笔记。正好最近对JMM比较感兴趣,就整理出了这篇文字。

    二、序言

    即使你的程序没有显示的创建任何线程,框架也可能为你创建了一些线程,这些线程调用的代码必须是线程安全(thread safe)的。这一点给开发人员的设计和实现赋予了更重要的一分责任。

    本文上下两篇,主要通过图文结合的方式描述了如下五个方面,希望有所帮助:

    • Java内存模型
    • 指令重排序
    • 顺序一致性模型
    • volatile内存语义与实现
    • 锁内存语义与实现

    三、正文

    1. Java内存模型

    在并发编程中,需要处理两个关键的问题:线程之间如何 通信 及线程之间如何 同步


    通信

    通信是指线程之间以何种机制来交换信息。在命令式编程语言中,线程之间的通信机制有两种方法:共享内存消息传递

    共享内存

    这种并发模型中,线程之间通过写-读内存中的公共状态来进行隐式通信。Java底层就是采用的这种方式。

    消息传递

    这种并发模型中,线程之间没有公共状态,线程之间必须通过发送消息来显示进行通信。如:流行的Actor模型。

    同步

    Java线程之间的通信由Java内存模型控制(简称JMM),JMM决定一个线程对共享变量的写入,何时对另一个线程可见。

    JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存中,每个线程都有一个本地内存(Local Memory),本地内存中存储了该线程读/写共享变量的副本。

    由此可见,线程A与线程B要通信的话,必须经历2个步骤。

    a.线程A将本地内存中的副本刷写到主内存中去。
    b.线程B从主内存读取已被A更新过的共享变量到B的本地内存
    

    那么,只要有其中一个环节没有完成,线程间的可见性就得不到保障,造成数据的不准确。

    2. 指令重排序

    2.1 什么是指令重排序?

    在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序。

    2.2 指令重排序的条件

    2.2.1 数据依赖性
    如果两个操作访问统一个变量,且其中一个为写操作,那么这两个操作之间就存在数据依赖性。

    重排序在以上三种情况下不会触发。

    2.2.2 as-if-serial语义
    as-if-serial语义的意思是:不管怎么重排序,(单线程)程序的执行结果不能被改变。

    我们通过一个计算圆面积的例子,说明这个问题:

    可以看到,无论如何重排序,程序的结果是不变的,准确的。

    以上,我们讨论了单线程情况下重排序的影响。(好像与我们写程序期望的目标一致: P),对于多线程程序,就未必如此了。


    我们也是通过一个简单的程序来说明该影响:

    假设有两个线程A和B,A先执行writer方法,然后B执行reader方法。
    我们可以分析得到,这两个方法内的代码都不符合数据依赖性原则,因此都有可能被重排序。

    重排序造成的执行路径有很多种,我们这里拿出其中的两种来说明问题。


    读取到了未赋值的a变量

    说明:int i = a * a;
    这个操作可以分成两个步骤,首先计算 a * a,接着赋值给变量i。
    

    大家可以在脑中计算下这两种情况下变量i的值。显然,都不符合我们写代码期望的结果。我们初步认识到了,重排序对于多线程情况下的可能造成的负面影响。

    3. 顺序一致性模型

    上面我们初步认识到了重排序可能对多线程的负面影响,那么如何避免此种情况的发生呢?

    我们下篇接着讨论: D.

    如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,谢谢。欢迎转载,请注明出处。
  • 相关阅读:
    centos 7安装libreoffice
    python3-xlwt-Excel设置(字体大小、颜色、对齐方式、换行、合并单元格、边框、背景、下划线、斜体、加粗)
    PHP导出身份证号科学计数法
    PHP接收json格式的POST数据
    微信小程序知识
    搭建Vue开发环境的步骤
    公众号认证?小程序认证?小程序复用公众号资质进行认证?
    七牛云——批量将本地图片上传到七牛云
    身份认证接口
    php二维数组去重
  • 原文地址:https://www.cnblogs.com/houqian/p/5709063.html
Copyright © 2020-2023  润新知