新的c ++标准引入了内存模型的概念。 关于它的问题已经有了,这是什么意思,它是如何改变我们在c ++中编写代码的方式等等。 我有兴趣了解C ++内存模型如何与旧的,众所周知的Java内存模型(1.5)相关联。 这是一样的吗? 这是类似的吗? 他们有什么重大的区别? 如果是这样,为什么? Java内存模型已经存在了很长时间,很多人都很了解它,所以我想这不仅对我来说,学习C ++内存模型是有帮助的。
在“Java并发实践”一书中,我们多次被告知,我们程序的指令可以由编译器,JVM在运行时甚至处理器进行重新sorting。 所以我们应该假定被执行的程序不会按照我们在源代码中指定的顺序执行它的指令。 然而,讨论Java内存模型的最后一章提供了“ 发生之前”规则的列表,指示JVM保留哪些指令sorting。 第一条规则是: “程序顺序规则。线程中的每个动作都发生在该线程中的每个动作之前,这些动作在程序顺序中稍后进行。 我相信“程序顺序”是指源代码。 我的问题是 :假设这条规则,我想知道什么样的指令可能会被重新sorting。 “行动”定义如下: Java内存模型是根据操作来指定的,包括对variables的读写操作,监视器的locking和解锁,以及与线程的启动和连接。 JMM定义了在程序中的所有操作之前发生的部分sorting。 为了保证执行动作B的线程可以看到动作A的结果(不pipeA和B是否出现在不同的线程中),在A和B之间的关系之前必定有一个事件发生。如果在两个操作,JVM可以自由地对其进行重新sorting。 其他提到的订单规则是: 监视locking规则。 在同一监视器锁上的每个后续锁之前,都会发生监视器锁的解锁。 易变的规则。 写入到易失性字段会在每次后续读取相同字段之前发生。 线程启动规则。 线程上的Thread.start调用在启动线程中的每个操作之前发生。 线程终止规则。 线程中的任何动作发生在任何其他线程检测到线程已终止之前,无论是从Thread.join成功返回还是由Thread.isAlive返回false。 中断规则。 另一个线程上的线程调用中断发生在被中断的线程检测到中断之前(通过抛出InterruptedException或者调用isInterrupted或者中断)。 终结者规则。 对象的构造函数的结束发生在该对象的终结器开始之前。 及物。 如果A发生在B之前,B发生在C之前,那么A发生在C之前。
重要的编辑我知道在发生两个任务的线程 “发生之前”我的问题是有可能是另一个线程读取“b”非空,而“a”仍然为空。 所以我知道,如果您从之前调用setBothNonNull(…)的线程调用doIt() ,那么它不能抛出NullPointerException exception 。 但是如果从另一个线程调用doIt() 而不是调用setBothNonNull(…) ? 请注意,这个问题是关于volatile关键字volatile保证:它不是关于synchronized关键字(所以请不要回答“你必须使用同步”,因为我没有任何问题来解决:我只是想了解有关无序执行的volatile担保(或缺乏担保)。 假设我们有一个包含两个volatilestring引用的对象,这些引用被构造函数初始化为null,并且我们只有一种方法来修改这两个string:通过调用setBoth(…)并且我们只能将它们的引用设置为非-null引用(只允许构造函数将它们设置为null)。 例如(这只是一个例子,现在还没有问题): public class SO { private volatile String a; private volatile String b; public SO() { a = null; b = null; } public void setBothNonNull( @NotNull final String one, @NotNull final String two ) { a = one; b = two; } public […]
评论接受的答案 这个问题产生的热量比我想象中的要多得多。 一个重要的结论,我从与并发利益邮件列表成员(即实际工作的人)的公共和私人讨论中吸取了一些重要的结论: 如果你可以find一个顺序一致的重新sorting,不会中断任何线程间发生的关系,这是一个有效的重新sorting(即符合程序顺序规则和因果关系要求)。 约翰·文特(John Vint)在他的回答中提供了这一点。 原来的问题 下面的代码(Java Concurrency in Practice列表16.3)不是线程安全的,原因很明显: public class UnsafeLazyInitialization { private static Resource resource; public static Resource getInstance() { if (resource == null) resource = new Resource(); // unsafe publication return resource; } } 然而,在几页之后的16.3节中,他们指出: 如果 Resource不可变, UnsafeLazyInitialization实际上是安全的。 我不明白这个说法: 如果Resource是不可变的,那么任何观察resourcevariables的线程将会看到它为null或完全构造(由于Java Memory模型提供的最终字段的强有力的保证) 然而,没有什么可以防止指令重新sorting:特别是两个resource读取可以重新sorting(有一个读取的if和return )。 所以一个线程可以在if条件中看到一个非null的resource ,但返回一个空引用(*)。 我认为即使Resource是不可变的, UnsafeLazyInitialization.getInstance()也可以返回null。 是这样,为什么(或为什么不)? 注:我期望有一个有争议的答案,而不是单纯的是或否的陈述。 (*)为了更好地理解我关于重新sorting的观点,作为JLS第17章并发性之一的作者之一的Jeremy […]
我正在开发涉及大量并发编程的Android项目,我将实现一些自定义的线程间通信(来自java.util.concurent的一个不适合我的目的)。 并发编程并不容易,但对于Dalvik而言似乎更难。 要获得正确的代码,您应该了解一些具体的事情,以及Dalvik出现的问题。 我只是找不到有关Dalvik VM的详细文档。 大多数Android资源(甚至是developer.android.com都关注平台API,并不提供任何关于一些不平凡(或低级)事物的深层信息)。 例如,Dalvik VM符合哪个版本的Java语言规范 ? 根据回答, volatilevariables的处理是不同的,并影响使用volatilevariables的任何并发代码。 已经有一些相关的问题了: Dalvik的内存模型和Java的一样吗? 在Android中双击lockinglocking fadden的一些答案是非常有用的,但我仍然想要得到更详细和完整的问题的理解。 所以在下面的一个原始问题我很有趣(我会更新列表,如有必要,以前的问题的答案将到达): 在哪里可以find有关Dalvik VM的详细信息,可以为下面的问题提供答案。 Dalvik VM符合哪个版本的Java语言规范 ? 如果对(2)的回答是“第三版”,那么在本规范中Dalviks对Java存储器模型的支持有多完善呢? 特别是如何完成对volatilevariables语义的支持? 在Android的Double checked locking中 , fadden提供了以下注释: 对。 随着“易变”关键字的增加,这将工作在单处理器(所有版本的Android)和SMP(3.0“蜂窝”和更高版本) 这是否意味着具有双核CPU但只有Android 2.3的三星Galaxy SII可能会错误地执行并发代码? (当然,Galaxy仅仅是一个例子,问题在于Android 3.0平台之前的多核设备) 在Dalvik的内存模型中是否和Java一样? 法登提供了以下句子的答案: 目前没有任何Dalvik版本对于JSR-133来说是完全正确的 这是否意味着任何现有的正确的并发Java代码可能在发布此评论之前发布的任何Android版本上工作不正确? 更新#1:回答@ gnat的评论(太长,以至于评论) @gnat发表评论: @Alexey Dalvik不符合任何JLS版本,因为一致性要求传递不是Dalvik选项的JCK。 这是否意味着你甚至不能使用标准的Java编译器,因为它符合标准规范? 那很重要吗? 如果是的话,怎么样? 那么,我的问题是不明确的。 我的意思是, JLS不仅是Java编译器实现的规则,而且是任何JVM实现的隐含指导。 事实上,例如, JLS指出,某些types的读写是primefaces操作。 编译器编写器不是很有趣,因为读/写只是转换成一个操作码。 但对于任何应该正确实现这些操作码的JVM实现来说,这是非常重要的。 现在你应该看看我在说什么。 […]
我已经看了一下CopyOnWriteArrayList OpenJDK源代码 ,看起来所有的写操作都被相同的锁保护,读操作根本不受保护。 据我所知,在JMM下,所有对variables(读取和写入)的访问都应该受到锁的保护,否则可能会发生重新sorting效应。 例如, set(int, E)方法包含这些行(在locking下): /* 1 */ int len = elements.length; /* 2 */ Object[] newElements = Arrays.copyOf(elements, len); /* 3 */ newElements[index] = element; /* 4 */ setArray(newElements); 另一方面get(int)方法只return get(getArray(), index); 。 在我对JMM的理解中,这意味着如果语句1-4像1-2(新)-4-2(copyOf)-3一样被重新sorting, get可能会观察到数组处于不一致状态。 我是否不正确地理解JMM,或者有什么解释说明为什么CopyOnWriteArrayList是线程安全的?
今天我实验室的一个敏感操作完全错了。 电子显微镜上的执行器越过了边界,经过一连串的事件,我损失了1200万美元的设备。 我已经在故障模块中缩小了40多条线路,这是因为: import java.util.*; class A { static Point currentPos = new Point(1,2); static class Point { int x; int y; Point(int x, int y) { this.x = x; this.y = y; } } public static void main(String[] args) { new Thread() { void f(Point p) { synchronized(this) {} if (p.x+1 != py) { […]
Dalvik的内存模型和Java的一样吗? 我特别感兴趣的是参考和非long /非double原始variables的读写是否是primefaces的,但是我也想知道两个平台的内存模型之间是否有区别。
必须不可变的对象的所有属性是final ? 据我所知没有。 但我不知道,我是否正确。