精华内容
下载资源
问答
  • 内存屏障和volatile关键字有什么关系?多线程环境下volatile关键字是怎样处理工作内存?怎样保证多个处理器对应缓存都是有效?总结 概念 关键字volatile可以说是Java虚拟提供最轻量级同步机制。 ​ Java...

    概念

    关键字volatile可以说是Java虚拟中提供的最轻量级的同步机制。
    ​ Java内存模型对volatile专门定义了一些特殊的访问规则。

    主要规则

    假定T表示一个线程,v和W分别表示两个volatile修饰的变量,那么进行read,load,use,assign,store和write操作的时候需要满足以下条件:

    保证可见性

    • 只有当线程T都变量V执行的前一个操作时load时,才能对V执行use操作;通知只有线程T对变量V执行的后一个操作是use时,线程T对变量V才能执行load操作。所以,线程T对变量的use动作和线程T对变量V的read,load动作相关联,必须一起出现。即一个线程在使用一个变量之前必须从主内存中获取到该变量的最新值,用于保证线程T能看得见其它线程对变量V的最新的修改后的值。
    • 只有当线程T对V执行的前一个动作时assign时,对V执行的后一个动作才能时store操作,同时只有当T对V执行的后一个操作时store时,前一个动作才能时assign操作。 所以,线程对变量的assign操作和线程T对变量的store,write操作是相关联的,必须同时出现。即在T的工作内存中,每次修改变量V之后必须立刻同步会主内存,用于保证线程T对V变量的修改能被别的线程看到。

    注意:
    对于普通的变量来说不一定能保证变量的可见性,因为某个线程在其工作内存中对普通变量做了修改之后并不能保证何时将新的值写回主存中,当另一个线程读取变量值的时候并不能保证主存中的值一定是新的值,有可能是上一个线程修改之前的旧值。


    保证有序性

    • 在同一个线程内部,被volatile修饰的变量不会被指令重排序, 保证代码的执行顺序和程序的顺序相同。

    不能保证原子性

    前面提到了volatile关键字可以保证被操作变量的可见性和有序性,但是为什么不能保证原子性?
    其实,对于一个单一的读/写操作,这里的单一指的是某一个操作只由一条指令组成,这种情况下是可以保证原子性的。因为完成一个操作之后将立即将刷新后的值从工作内存存入到主内存中,读取一个变量之前必须从主内存中获取最新的值。
    但是对于一些复合操作,是不具有原子性的,例如volatile关键字修饰的变量进行自增的操作就不具备原子性。

    解释之前,首先我们来明确几个概念:

    内存屏障

    什么是内存屏障?

    1. 用来确定一些特定操作的执行顺序
    2. 影响一些数据的可见性。

    内存屏障有什么作用?

    1. 命令cpu和编译器必须先执行某个命令或者必须后执行某个命令。
    2. 强制更新一次不同的cpu缓存。

    内存屏障和volatile关键字有什么关系?

    对于一个被volatile关键字修饰的变量而言:

    1. 读屏障:在对这个变量进行读操作之前,会插入一个读屏障,保证在读取数据前所有的事件已经发生 ,并且任何更新过的数据值是可见的,因为内存屏障会把之前的写入值都刷新到缓存。
    2. 写屏障:在写操作转换后插入一个写屏障指令,保证当前访问这个字段的所有线程将会得到最新值。

    多线程环境下volatile关键字是怎样处理工作内存的?

    1. 使用volatile关键字修饰的变量在被修改后会强制立即写入主存中。
    2. 对于多个线程,如果线程2在线程1操作变量时也修改了相同变量的值,会导致线程1中的缓存(工作内存)失效。
    3. (接第二条)如果此时线程1中的缓存失效了,则当线程1再次读取数据时,将从主存中获取数据的最新值。

    怎样保证多个处理器对应的缓存都是有效的?

    在多个处理器处理的情况下,为了保证缓存的一致性,就实现了缓存一致协议,每个处理器会通过嗅探在总线上的数据来检查自己的缓存是不是过期了,如果已经过期了,就会将当前的缓存设置为无效状态,当处理器对这个数据进行修改操作时,就会重新从系统内存中把数据读到缓存中。

    同时,需要明确的一点是,对于一个变量的自增操作,其实伴随着三个步骤:

    • 读取原值到工作内存中
    • 变量自增
    • 将自增之后的值写回主存中

    现在来看一个例子:

    public class TestVo {
        public static volatile int inc = 0;
        public static void f() {
            for (int j = 0; j < 1000; j++) {
                TestVo.increase();
            }
        }
        public static void increase() {
            inc++;
        }
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new Thread(()->{
                    TestVo.f();}).start();
            }
            //使当前线程由执行状态变为就绪状态
            //让出cpu的执行时间,让下一个线程执行的时候,该线程可能执行了,也可能没有执行。
            while (Thread.activeCount()>2){
                Thread.yield();
            }
           System.out.println(TestVo.inc);
        }
    }
    
    9285
    
    Process finished with exit code 0
    
    
    8166
    
    Process finished with exit code 0
    

    由上述结果可以看出,当开辟多个线程对一个volatile变量进行自增时,每次运行的结果都是一个小于10000的值,而不是我们预期的按照理论计算得到的10000。
    为什么会产生这样的结果?

    假设现在inc的值为10,这时线程A将对其进行自增操作,线程A此时读取了inc的值到其工作内存中,值为10,但此时线程A被阻塞了,线程B也要进行自增操作,因为线程A没有对inc的值进行修改,所以B的缓存是有效的,读取到的值也为10。
    所以B对inc的值进行自增,自增之后inc的值为11,并将其最新值写回主存,但是线程A的工作内存中已经读取到了inc的值为10,所以A进行了自增之后其值也变成了11。
    所以,尽管两个线程对同一个变量都进行了自增操作,但是其自增之后的值还是自增一次之后的效果,而不是两次。

    一开始我也有疑问,当线程A进行自增时,不应该从主存中获取最新的值11吗?但其实不是的,因为对于自增操作,读取,自增,写入这三个操作合起来算一个完整的操作,所以,当线程A读取了inc的值时,相当于进入了这个自增操作,其后的自增和写入内存都属于这一个操作里的分操作,在这个完整的操作结束之前,是不能从主存中获取新值的。

    现在看例二:

    //线程1
    boolean volatile stop = false;
    while(!stop){
        doSomething();
    }
     
    //线程2
    stop = true;
    

    对于这个例子,同样的,当线程2将stop的值改为true之后,线程1中对stop值的缓存将变得无效,而对于while循环语句中的操作,每次的判断都是一个单一的操作,所以线程1能立即感知到stop的缓存变得无效,并且会从主存中重新获取其最新值。
    这也就印证了之前说的,对于单一读/写操作,是满足原子性的。

    总结

    1.对于单一的读或写操作,volatile是满足原子性的。
    2.对于复合操作例如++操作,是不满足原子性的。
    3.volatile关键字能够保证数据的可见性和顺序性。

    展开全文
  • Java Bean实际上相当于微软COM模型中的本地进程内COM组件,它是不能被跨进程访问的。Enterprise Java Bean 相当于DCOM,即分布式组件。它是基于Java的远程方法调用(RMI)技术的,所以EJB可以被远程访问(跨进程、跨...
  • 中的范例在Windows XP系统、Macintosh OS X系统和Linux系统上进行 了测试。只有为数不多的几个程序会受编译器不兼容问题的影响。本书前一版面世后,编译器在遵循C++标 准方面更严格。 对于本书中完整的程序,...
  • 中的范例在Windows XP系统、Macintosh OS X系统和Linux系统上进行 了测试。只有为数不多的几个程序会受编译器不兼容问题的影响。本书前一版面世后,编译器在遵循C++标 准方面更严格。 对于本书中完整的程序,...
  • 中的范例在Windows XP系统、Macintosh OS X系统和Linux系统上进行 了测试。只有为数不多的几个程序会受编译器不兼容问题的影响。本书前一版面世后,编译器在遵循C++标 准方面更严格。 对于本书中完整的程序,...
  • 中的范例在Windows XP系统、Macintosh OS X系统和Linux系统上进行 了测试。只有为数不多的几个程序会受编译器不兼容问题的影响。本书前一版面世后,编译器在遵循C++标 准方面更严格。 对于本书中完整的程序,...
  • C#与.NET3.5高级程序设计第四版高清PDF中文完整版

    千次下载 热门讨论 2011-07-05 10:25:50
    4.3 c#中的数组操作  4.4 枚举类型  4.5 结构类型  4.6 值类型和引用类型  4.7 值类型和引用类型:最后的细节  4.8 c#可空类型  4.9 小结  第5章 定义封装的类类型  5.1 c#类类型  5.2 类构造...
  • 1.1.1 Java有什么优势? 2 1.1.2 Java在哪儿? 3 1.2 准备好开始Java之旅 3 1.2.1 下载JDK 4 1.2.2 安装JDK 5 1.2.3 配置环境变量 6 1.2.4 测试环境是否安装成功 8 1.2.5 如果失败了怎么办? 9 1.3 让自己...
  • 1.1.1 Java有什么优势? 2 1.1.2 Java在哪儿? 3 1.2 准备好开始Java之旅 3 1.2.1 下载JDK 4 1.2.2 安装JDK 5 1.2.3 配置环境变量 6 1.2.4 测试环境是否安装成功 8 1.2.5 如果失败了怎么办? 9 1.3 让自己...
  • 1.1.1 Java有什么优势? 2 1.1.2 Java在哪儿? 3 1.2 准备好开始Java之旅 3 1.2.1 下载JDK 4 1.2.2 安装JDK 5 1.2.3 配置环境变量 6 1.2.4 测试环境是否安装成功 8 1.2.5 如果失败了怎么办? 9 1.3 让自己...
  • 你必须知道495个C语言问题

    千次下载 热门讨论 2015-05-08 11:09:25
    1.12 关键字auto到底有什么用途? 类型定义(typedef) 1.13 对于用户定义类型,typedef和#define有什么区别? 1.14 我似乎不能成功定义一个链表。我试过typedefstruct{char*item;NODEPTRnext;}*NODEPTR;但是...
  • 21天学通C++ (中文第五版)

    热门讨论 2010-06-23 16:57:03
    9.11 返回指向不在作用中的对象的引用 9.12 指针归谁所有 9.13 小结 9.14 问与答 9.15 作业 9.15.1 测验 9.15.2 练习 第10章 有关函数的高级主题 10.1 重载成员函数 10.2 使用默认值 10.3 在默认值和...
  • LINGO软件学习

    2009-08-08 22:36:50
    在一个模型中,原始集是基本对象,不能再被拆分成更小组分。原始集可以由显式罗列和隐式罗列两种方式来定义。当用显式罗列方式时,需在集成员列表中逐个输入每个成员。当用隐式罗列方式时,只需在集成员列表中...
  • 7.6.2 方法和作用中的内部类 7.6.3 链接到外部类 7.6.4 static内部类 7.6.5 引用外部类对象 7.6.6 从内部类继承 7.6.7 内部类可以覆盖吗? 7.6.8 内部类标识符 7.6.9 为什么要用内部类:控制框架 7.7 构建器和...
  • java联想(中文)

    2008-12-29 21:07:11
    7.6.2 方法和作用中的内部类 7.6.3 链接到外部类 7.6.4 static内部类 7.6.5 引用外部类对象 7.6.6 从内部类继承 7.6.7 内部类可以覆盖吗? 7.6.8 内部类标识符 7.6.9 为什么要用内部类:控制框架 7.7 构建器和...
  • Python核心编程第二版(中文)

    热门讨论 2015-04-23 16:40:13
    6.18.5 字典的关键字 6.19 相关模块 6.20 *拷贝Python对象、浅拷贝和深拷贝 6.21 序列类型小结 6.22 练习 第7章 映像和集合类型 7.1 映射类型:字典 7.1.1 如何创建字典和给字典赋值 7.1.2 如何访问字典...
  • 7.6.2 方法和作用中的内部类 7.6.3 链接到外部类 7.6.4 static内部类 7.6.5 引用外部类对象 7.6.6 从内部类继承 7.6.7 内部类可以覆盖吗? 7.6.8 内部类标识符 7.6.9 为什么要用内部类:控制框架 7.7 构建器和...
  • 3.4.5 C++11中的auto声明 3.5 总结 3.6 复习题 3.7 编程练习 第4章 复合类型 4.1 数组 4.1.1 程序说明 4.1.2 数组的初始化规则 4.1.3 C++11数组初始化方法 4.2 字符串 4.2.1 拼接字符串常量 4.2.2 在...
  •  C++作为一门高级程序设计语言,可说是历史悠久,算得上是程序设计语言中的“老革命”了。了解C++的发展历史,可以加深我们对这门语言的认识,了解C++的本质内涵,了解C++的文化,从而更好地学习和掌握这门语言。
  • 1.12 关键字auto到底有什么用途? 7 类型定义(typedef) 7 1.13 对于用户定义类型,typedef 和#define有什么区别? 7 1.14 我似乎不能成功定义一个链表。我试过typedef struct{char *item; NODEPTR next;}* ...
  • Python核心编程(中文第二版)

    热门讨论 2009-10-02 12:08:14
     6.18.5 字典的关键字   6.19 相关模块   6.20 *拷贝Python对象、浅拷贝和深拷贝   6.21 序列类型小结   6.22 练习   第7章 映像和集合类型   7.1 映射类型:字典   7.1.1 如何创建字典和给...
  • 《你必须知道495个C语言问题》

    热门讨论 2010-03-20 16:41:18
    1.12 关键字auto到底有什么用途? 7 类型定义(typedef) 7 1.13 对于用户定义类型,typedef 和#define有什么区别? 7 1.14 我似乎不能成功定义一个链表。我试过typedef struct{char *item; NODEPTR next;}* ...
  • 15、关系模型的数据操纵既是建立在关系上数据操纵,一般(插入)、增加、删除、和修改四种操作。 16、TIME()返回值数据类型是(String)类型。 17、编写SQL语句 1)、创建一张学生表,包含以下...
  • 是非常详细和深入Vb6学习教程,无论对初学者还是经验开发人员,都非常帮助 第一部分基础篇1 第1章 VB6入门1 1.1 集成开发环境1 1.1.1 运行IDE1 1.1.2 选择工程类型1 1.1.3 IDE窗口2 1.1.4 菜单5 1.1.5 工具...
  • 是非常详细和深入Vb6学习教程,无论对初学者还是经验开发人员,都非常帮助 第一部分基础篇1 第1章 VB6入门1 1.1 集成开发环境1 1.1.1 运行IDE1 1.1.2 选择工程类型1 1.1.3 IDE窗口2 1.1.4 菜单5 1.1.5 工具...
  • 1.2.8 对大数据平台中的元数据管理是怎么理解的,元数据收集管理体系是怎么样的,会对大数据应用有什么样的影响 1.2.9 你理解常见如阿里,和友商大数据平台的技术体系差异以及发展趋势和技术瓶颈,在存储和计算两...
  • 网上高清版350M。可以去下 http://115.com/file/be5gwid8 请于下载后 24H 内及时删除!请抱着学习态度下载此资料。 总共900多页!!!!!!! 第1篇 技术篇 第1章 大型门户网站架构分析 3 1.1 大型门户...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 138
精华内容 55
关键字:

关系模型中的关键字有什么作用