基本内存屏障

  处理器支持那种内存重排序,就会提供能够禁止相应内存重排序的的指令,这些指令就被成为基本内存屏障:StroeLoad屏障、StroeLoad屏障、LoadLoad屏障、LoadStore屏障。基本内存屏障是对一类指令的称呼(可以用XY来标识),这类指令的作用是禁止该指令左侧的X操作与该指令右侧的Y操作之间进行重排序,从而确保该指令左侧的所有X操作先于该指令右侧的Y操作被提交,即内存操作作用到主内存(或高速缓存)上。基本内存屏障只是保障其左侧的X操作先于右侧的Y操作被提交,但是它并不完全禁止重排序,XY屏障两侧的内存操作仍然可以在不越过内存屏障本身的情况下在各自范围内进行重排序,并且XY屏障左侧的非X操作和屏障右侧的非Y操作之间仍可以重排序。

基本内存屏障的具体作用

屏障名称 示例指令序列 具体作用
StroeLoad

Store1,Store2,Store3,

StoreLoad,Load1,Load2,Load3

禁止StoreLoad重排序,即确保该屏障之前的任何一个写操作的结果都会在该屏障之后任何一个读操作的数据被加载之前对其他处理器来说是可同步的
StoreStore

Store1,Store2,Store3,

StoreStore,Store4,Store5,Store6

禁止StoreStore重排序,即确保该屏障之前的任何一个写操作的结果都会在该屏障之后任何一个写操作之前对其他处理器来说是可同步的
LoadLoad

Load1,Load2,Load3,

LoadLoad,Load4,Load5,Load6

禁止LoadLoad重排序,即确保该屏障之前的任何一个读操作的数据都会在该屏障之后的任何一个读操作之前被加载
LoadStore

Load1,Load2,Load3,

LoadStore,Store1,Store2,Store3

禁止LoadStore重排序,确保该屏障之前的任何一个读操作的数据都会在该屏障之后的任何一个写操作的结果被冲刷到高速缓存(或主内存)之前被加载
  • StroeLoad 屏障会清空无效化队列,并将写缓冲器中的条目冲刷到高速缓存。因此,StroeLoad 屏障既可以将其他处理器对共享变量所做的更新同步到该处理器的高速缓存中,又可以使其处理器对共享变量所做的共享对其他处理器来说是同步的。
  • StoreStore屏障是通过对写缓存器中的条目进行标记来实现禁止StoreStore重排序的。StoreStore屏障会将写缓冲器中的现有条目做一个标记,以表示这些条目代表的写操作需要先于该屏障之后的写操作被提交处理器在执行写操作时如果发现写缓冲器中存在被标记的条目,那么即使这个写操作对应的高速缓存条目的状态为M或E,也不直接写入高速缓存,而是写入写缓冲器,保证StoreStore屏障之前的写操作先于之后的写操作被提交。
  • LoadLoad屏障是通过清空无效化队列来实现禁止LoadLoad重排序。LoadLoad屏障会使其执行处理器根据无效化队列中的Invalidate消息删除其高速缓存中相应的副本。是处理器有机会将其他处理器对共享变量所做的更新同步到该处理器的高速缓存中,从而消除了LoadLoad重排序重排序的根源。

Java内存模型

  Java内存模型定义了final、volatile、synchronized关键字的行为,并确保正确同步的Java程序能够正确的运行在不同架构的处理器上。它从“什么”的角度解答了以下几个线程安全问题。

  • 原子性问题:针对实例变量、静态变量(即共享变量非局部变量)的读、写操作,哪些是具备原子性的,哪些可能不具备原子性。
  • 可见性问题:一个线程对实例变量、静态变量进行的更新在什么情况下能够被其他线程所读取。
  • 有序性问题:一个线程对多个实例变量、静态变量进行的更新在什么情况下在其他线程看来可以是乱序的。

  在原子性方面,Java内存模型规定了对long、double型以外的基本数据类型和引用数据类型的共享变量进行读、写操作都是具有原子性的。特别的,对于volatile修饰的long、double型共享变量进行读、写操作也是具有原子型的。可见性和有序性,Java内存模型是通过happens-before这个术语解答的。

happens-before关系

  假设动作A happens-before 动作B,那么Java内存模型机会保证A的操作结果对B是可见的,即A的操作结果会在B被执行请提交。happens-before关系具有传递性,如果A happens-before B,B happens-before C,则A happens-before C。Java内存模型定义了一些有关happens-before关系的规则,这些规则规定了两个动作在什么情况下具有happens-before关系,如下所示。

  • 程序顺序规则:一个线程中每个动作都happens-before该线程中程序顺序上排在该动作之后的每一个动作。
  • 内部锁规则:内部锁的释放happens-before后续每一个对该锁的申请。释放和申请必须是针对同一锁实例。
  • volatile变量规则:对一个volatile变量的写操作happens-before后续每一个对该变量的读操作。
  • 线程启动规则:调用一个线程的start方法happens-before被启动这个线程中的任何一个动作。
  • 线程终止规则:一个线程中任何一个动作都happens-before该线程的join方法的执行线程在join方法返回之后所执行的任意一个动作。

Java多线程学习笔记之三内存屏障与Java内存模型的更多相关文章

  1. java多线程学习笔记——详细

    一.线程类  1.新建状态(New):新创建了一个线程对象.        2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中, ...

  2. JAVA多线程学习笔记(1)

    JAVA多线程学习笔记(1) 由于笔者使用markdown格式书写,后续copy到blog可能存在格式不美观的问题,本文的.mk文件已经上传到个人的github,会进行同步更新.github传送门 一 ...

  3. Java多线程学习笔记(一)——多线程实现和安全问题

    1. 线程.进程.多线程: 进程是正在执行的程序,线程是进程中的代码执行,多线程就是在一个进程中有多个线程同时执行不同的任务,就像QQ,既可以开视频,又可以同时打字聊天. 2.线程的特点: 1.运行任 ...

  4. Java多线程学习笔记

    进程:正在执行中的程序,其实是应用程序在内存中运行的那片空间.(只负责空间分配) 线程:进程中的一个执行单元,负责进程汇总的程序的运行,一个进程当中至少要有一个线程. 多线程:一个进程中时可以有多个线 ...

  5. Java多线程学习笔记--生产消费者模式

    实际开发中,我们经常会接触到生产消费者模型,如:Android的Looper相应handler处理UI操作,Socket通信的响应过程.数据缓冲区在文件读写应用等.强大的模型框架,鉴于本人水平有限目前 ...

  6. java 多线程学习笔记

    这篇文章主要是个人的学习笔记,是以例子来驱动的,加深自己对多线程的理解. 一:实现多线程的两种方法 1.继承Thread class MyThread1 extends Thread{ public ...

  7. Java 多线程学习笔记:生产者消费者问题

    前言:最近在学习Java多线程,看到ImportNew网上有网友翻译的一篇文章<阻塞队列实现生产者消费者模式>.在文中,使用的是Java的concurrent包中的阻塞队列来实现.在看完后 ...

  8. java多线程学习笔记(七)

    volatile关键字 关键字volatile的主要作用是使变量在多个线程间可见. public class PrintString { private boolean isContinue = tr ...

  9. java多线程学习笔记(四)

    上一节讲到Synchronized关键字,synchronized上锁的区域:对象锁=方法锁/类锁 本节补充介绍一下synchronized锁重入: 关键字synchronized拥有锁重入的功能,也 ...

随机推荐

  1. VS2012 生成项目报 "Lc.exe已退出,代码为-1" 错误

    解决方法:删除项目下Properties文件下的license.licx文件即可.

  2. 对于dll(动态链接库)的理解

    之前,尝试过写过dll,但是对于dll的理解还是不够深刻吧.今天,又加深了对于dll的理解程度,故记下以免以后忘记. 无论是c还是c++,我们通常先将源文件编译成中间代码,在Windows下是&quo ...

  3. 理解nodejs的module模块儿

    module 在 Node.js 模块系统中,每个文件都视为独立的模块,node在运行某个模块儿时会生成一个module对象 Module { id: '.', exports: 2, parent: ...

  4. MVC应用程序JsonResult()的练习

    这次学习MVC应用程序,尝试使用jQuery的获取数据,并显示于视图上,关键技术还是javascript的一个子集Json.我们先去HomeController写一个方法JsonResult GetJ ...

  5. Spring源码分析:非懒加载的单例Bean初始化过程(上)

    上文[Spring源码分析]Bean加载流程概览,比较详细地分析了Spring上下文加载的代码入口,并且在AbstractApplicationContext的refresh方法中,点出了finish ...

  6. [日常] Go语言圣经-匿名函数习题

    Go语言圣经-匿名函数1.拥有函数名的函数只能在包级语法块中被声明,通过函数字面量(function literal),我们可绕过这一限制,在任何表达式中表示一个函数值2.通过这种方式定义的函数可以访 ...

  7. Java集合类源码解析:LinkedHashMap

    前言 今天继续学习关于Map家族的另一个类 LinkedHashMap .先说明一下,LinkedHashMap 是继承于 HashMap 的,所以本文只针对 LinkedHashMap 的特性学习, ...

  8. 深入理解读写锁ReentrantReadWriteLock

    1.读写锁的介绍 在并发场景中用于解决线程安全的问题,我们几乎会提供高频率的使用到独占式锁,通常使用java提供的关键字synchronized(关于synchronized可以看这篇文章)或者con ...

  9. logback日志的美化

    1.logback.xml如下 <?xml version="1.0" encoding="UTF-8" ?> <configuration& ...

  10. Java高级工程师需要弄明白的20个知识点

    一般的程序员或许只需知道一些JAVA的语法结构,能对数据库数据进行CRUD就可以应付了.但要成为JAVA(高级) 工程师,就要对JAVA做比较深入的研究,需要不断学习进步,以下对高级工程师需要突破的知 ...