最近看了两篇博文:
主题:java线程安全总结
主题:线程安全总结(二)
看完之后对自己触动很大,和自己以前的理解有很大的矛盾.当时发站内信给作者的疑惑内容如下:
写道
看完之后,有2个疑问:
⒈java内存模型,没有说内存模型的具体结构,如:heap,java stack,method area等.
2.关于"那么,何谓可见性? 多个线程之间是不能互相传递数据通信的,它们之间的沟通只能通过共享变量来进行。Java内存模型(JMM)规定了jvm有主内存,主内存是多个线程共享的。当new一个对象的时候,也是被分配在主内存中,每个线程都有自己的工作内存,工作内存存储了主存的某些对象的副本,当然线程的工作内存大小是有限制的。当线程操作某个对象时,执行顺序如下:
(1) 从主存复制变量到当前工作内存 (read and load)
(2) 执行代码,改变共享变量值 (use and assign)
(3) 用工作内存数据刷新主存相关内容 (store and write) JVM规范定义了线程对主存的操作指令:read,load,use,assign,store,write。当一个共享便变量在多个线程的工作内存中都有副本时,如果一个线程修改了这个共享变量,那么其他线程应该能够看到这个被修改后的值,这就是多线程的可见性问题。"
疑问:为什么要有很多的副本呢?然后再刷新到主存呢?这样做不是效率很低?
java中的方法是值传递,参数是对象的话,只是对象指针的一个副本,如:
public void fun(Object obj){
// 改变obj的属性
obj.property = "abc";
// 改变obj指针的值,对方法外面的obj无影响
obj = new Object();
obj.property = "def";
}
既然每个线程中java stack传递的都是指针(指向heap内的对象),为什么还要复制一个heap内对象的副本呢?
后来看到第二篇的时候
写道
看完之后明白了,我以前的理解只是基于内存中的模型,而你说的read,load,use,assign,store,write针对的是对CPU,寄存器,内存等统一抽象的工作内存的操作
谢谢分享!让我对线程安全的理解更深入一步
今天,特意的查询了这方面的内容,博客地址:http://kenwublog.com/illustrate-memory-reordering-in-cpu
写道
对主存的一次访问一般花费硬件的数百次时钟周期。处理器通过缓存(caching)能够从数量级上降低内存延迟的成本这些缓存为了性能重新排列待定内存操作的顺序。也就是说,程序的读写操作不一定会按照它要求处理器的顺序执行。
重排序的背景
我们知道现代CPU的主频越来越高,与cache的交互次数也越来越多。当CPU的计算速度远远超过访问cache时,会产生cache wait,过多的cache wait就会造成性能瓶颈。
针对这种情况,多数架构(包括X86)采用了一种将cache分片的解决方案,即将一块cache划分成互不关联地多个 slots (逻辑存储单元,又名 Memory Bank
或 Cache Bank),CPU可以自行选择在多个 idle bank 中进行存取。这种 SMP
的设计,显著提高了CPU的并行处理能力,也回避了cache访问瓶颈。
Memory Bank的划分
一般 Memory bank 是按cache address来划分的。比如 偶数adress 0×12345000 分到 bank 0, 奇数address 0×12345100 分到 bank1。
重排序的种类
编译期重排。编译源代码时,编译器依据对上下文的分析,对指令进行重排序,以之更适合于CPU的并行执行。
运行期重排,CPU在执行过程中,动态分析依赖部件的效能,对指令做重排序优化。
实例讲解指令重排序原理
为了方便理解,我们先来看一张CPU内部结构图。
从图中可以看到,这是一台配备双CPU的计算机,cache 按地址被分成了两块 cache banks,分别是 cache bank0
和 cache bank1
。
最后引用博客作者的一句话:
写道
先抛开java虚拟机不谈,我们都知道,现在的计算机,cpu在计算的时候,并不总是从内存读取数据,它的数据读取顺序优先级是:寄存器-高速缓存-内存。线程耗费的是CPU,线程计算的时候,原始的数据来自内存,在计算过程中,有些数据可能被频繁读取,这些数据被存储在寄存器和高速缓存中,当线程计算完后,这些缓存的数据在适当的时候应该写回内存。当个多个线程同时读写某个内存数据时,就会产生多线程并发问题,涉及到三个特性:原子性,有序性,可见性
而实现这个的就是编译期重排和运行时重排序
分享到:
相关推荐
ck - 并发原语、安全内存回收机制和非阻塞数据结构
参数说明:被创建进程的外部标识符n、初始CPU状态S0(包括CPU的工作方式、进程起始地址以及屏蔽码等)、进程优先数K0、初始内存M0以及所需资源的清单R0等、某进程运行的中间结果acc 创建过程如下: 首先,从PCB集合...
详细介绍java并发编程相关知识: 基础知识 并发与并行 Java并发演进历史 Java并发模型 线程模型 存储模型 JVM同步原语 volatile CAS 线程安全 保护“共享数据” 低级并发工具 原子变量 锁...
用Pascal语言实现P(s)和V(s)操作。
java语言操作系统课程设计模拟进程管理系统源码 需求分析 实现n个进程并发运行; 实现进程创建、撤销、阻塞、唤醒; 实现进程的同步; 实现优先级调度、时间片轮转、短进程优先等调度算法; 系统在运行过程中应能...
java线程之间的通信对程序员完全透明,内存可见性问题...三个同步原语(lock,volatile,final)的内存语义,重排序规则及在处理器中的实现;java内存模型的设计目标,及其与处理器内存模型和顺序一致性内存模型的关系
PV原语通过操作信号量来处理进程间的同步与互斥的问题。其核心就是一段不可分割不可中断的程序。 信号量的概念1965年由著名的荷兰计算机科学家Dijkstra提出,其基本思路是用一种新的变量类型(semaphore)来记录当前...
计算机操作系统PV原语分析,介绍PV原语通过操作信号量来处理进程间的同步与互斥的问题。其核心就是一段不可分割不可中断的程序……
Concurrent - 函数式并发原语的集合
1、"上海某大厂面试真题,掌握并发编程三大核心—原子性、可见性和有序性,是优化...#并发控制 #Java并发原语" 7、"精通Java线程调度算法,理解抢占式调度机制,更好地设计高性能多线程应用。#线程调度 #Java并发高级
详细介绍Java内存,ava线程之间的通信对程序员完全...三个同步原语(lock,volatile,final)的内存语义,重排序规则及在处理器中的实现;java内存模型的设计目标,及其与处理器内存模型和顺序一致性内存模型的关系。
模拟实现同步机构避免并发进程执行时可能出现的与时间有关的错误。 二. 实验目的 进程是程序在一个数据集合上运行的过程。进程是并发执行的,也即系统中的多个进程轮流的占用处理器运行。 我们把若干个进程都能进行...
你将理解 Go语言为何选定这些并发模型,这些模型又会带来什么问题,以及你如何组合利用这些模型中的原语去解决问题。学习那些让你在独立且自信的编写与实现任何规模并发系统时所需要用到的技巧和工具。 理解Go语言...
使用Golang原语的CPU缓存行无效 介绍 一直是并行性的陷阱。 对于在高速缓存行边界内分配的共享上下文上同时进行读/写更新的应用程序,性能上的损失变得明显。 现代CPU倾向于利用高速缓存来频繁更新内存位置。 CPU...
结构化并发的原语。 托儿所允许编写符合结构化并发的并发程序。 如果您不熟悉这个概念,那么专用的上有一些优秀的资源。 库名的灵感来自于优秀的 Python 。 目录 返回错误 恢复其他返回类型 恐慌 与 ...
如何用PV原语实现进程间的互斥与同步 P操作和V操作是不可中断的程序段,称为原语。PV原语及信号量的概念都是由荷兰科学家E.W.Dijkstra提出的。信号量sem是一整数,sem大于等于零时代表可供并发进程使用的资源实体...
用于操作系统的编程,运用P,V原语实现进程间同步与互斥
深入到Go语言的内存同步原语。 利用这些模式中的原语编写可维护的并发代码。 将模式组合成为一系列的实践,使你能够编写大规模的分布式系统。 学习 goroutine 背后的复杂性,以及Go语言的运行时如何将所有东西...
让我们更加容易的了解书本上的一些知识点,希望大家来看看
xilinx V6系列原语程序 包含所有原语 直接复制代码到工程文件即可