|
锁定老贴子 主题:多核线程笔记-volatile原理与技巧
该帖已经被评为精华帖
|
|
|---|---|
| 作者 | 正文 |
|
最后更新时间:2008-07-30
volatile, 用更低的代价替代同步
/**
* Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
*/
public final int getAndSet(int newValue) {
for (;;) {
int current = get();
if (compareAndSet(current, newValue))
return current;
}
}
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
|
|
| 返回顶楼 | |
|
最后更新时间:2007-08-06
从楼主文章链接的一些信息发现, volatile 在 1.5 以前的 JVM 中支持并不好, 好像是因为之前版本的Java规范并没有严格限定这个语义的实现.
想想也难怪, 高中时候用的 PC XT 机主存才 640KB, 老师的 286 也不过 1M, 而现在一颗 CPU 核心的内置 L2 缓存也大部分 1M 以上了, L1 缓存也有 32K. 就算主存是同步的, 这些核心的内部缓存也是个非常独立的小环境了. 不过好在 Java 的 synchronized 语义从一开始就规定得到位, 退出同步块之前要把所有线程局部存储都和主存同步. 这个虽然慢一些, 不过倒是可以保证在各个版本的 JVM 里都正确. |
|
| 返回顶楼 | |
|
最后更新时间:2007-08-06
是的, volatile仅仅在1.5以后的jvm中, 才得到修复.
早前使用synchrnoized的比较多! 但是测试发现, synchronized的效率没ReetranLock高. Lock同样是同步的替代. |
|
| 返回顶楼 | |
|
最后更新时间:2007-08-06
volatile 保证 各线程间共享数据的一致性,和数据操作的原子性,不能保证线程间同步。
且lz文章中所指 直接从内存存取、缓存概念,并不正确。请参考lz的 多核线程笔记-java内存模型详解 中working momery model的介绍。 |
|
| 返回顶楼 | |
|
最后更新时间:2007-08-06
Godlikeme 写道 volatile 保证 各线程间共享数据的一致性,和数据操作的原子性,不能保证线程间同步。
且lz文章中所指 直接从内存存取、缓存概念,并不正确。请参考lz的 多核线程笔记-java内存模型详解 中working momery model的介绍。 volatile修饰的变量, 将不通过cpu一二级缓存存储. (即不使用working memory) 直接通过内存, 才能保证变量的可见性. |
|
| 返回顶楼 | |
|
最后更新时间:2007-08-06
volatile和cpu一二级缓存和硬件实现没有关系吧,cpu一二级缓存对软件是透明的吧,cpu自己会管理,程序不可能读到不一样的。
引用 多线程中有主内存和工作内存之分, 在JVM中,有一个主内存,专门负责所有线程共享数据;而每个线程都有他自己私有的工作内存, 主内存和工作内存分贝在JVM的stack区和heap区。
volatile应该是一种软件机制。 |
|
| 返回顶楼 | |
|
最后更新时间:2007-08-06
解释一下
java的内存模型分为main memory和working memory。注:这里的memory不是真正的内存和cpu缓存,是一个抽象概念。 main memory是实力所在的区域,所有的实例都存在于main memory中。 working memory为各个线程所拥有的工作区,所有的线程都有其专有的working memory,working memory中存有main momery中必要部分的拷贝 。 volatile保证的是所有对声明实例的操作是原子的,是直接对main memory。 具体的情况不是几句话能说清,但从模型上讲,是抽象在物理设备之上的概念。具体jvm实现上肯定要考虑物理硬件指令,有听说jdk5中增加了这部分硬件的支持。但似乎lz混淆了这层抽象概念和具体物理存储。 可以参考java language spec中,memory model和volatile field部分。 |
|
| 返回顶楼 | |
|
最后更新时间:2007-08-07
volatile使编译器产生的汇编指令, 仅仅从主存操作数据.这点是肯定的.
working memory是否在缓存上这个问题,此前看到一些资料,获知: 多核处理器,每个处理器都会有自己的二级缓存, 那时, 如果其中一颗处理器的二级缓存里的变量被改动了, 其他的处理器是无法得知的. 只能再次通过内存交换数据. 这种情况,是否意味着工作内存使用了缓存?因为heap共享数据不会放缓存里. 而working memory确实是在java stack区中,但是,在多核环境下, 优化使用cpu cache,将是唯一提升点. (之前说法可能有点问题) 多线程操作,通过内存本身已经是比较低性能的了. 使用volatile,还有一部分是为了防止多核处理器动态重排序执行指令. |
|
| 返回顶楼 | |
|
最后更新时间:2007-08-07
是啊, 现代处理器内部流水线一般都很深, 重排指令进行流水线优化是任何编译器的重要任务, C 很早就明确规定 volatile 的含义了. 可能以前Java的重心并不在性能, 所以定义了synchronized就希望它成为universal的解决方案. 但是现在Java成势了, 性能开始成为重要环节了, 也就开始启用各种复杂机制了. 感觉 NIO, java.util.concurrent, volatile 的加入都有些这种原因.
另外好像 ReentrantLock 说只有在多CPU, 并且读取线程明显多于写入线程的情况下才比 synchronized 性能有明显提高. |
|
| 返回顶楼 | |
|
最后更新时间:2007-08-07
重申一遍问题:
1.volatile 保证 各线程间共享数据的一致性,和数据操作的原子性,不能保证线程间同步。 2.且lz文章中所指 直接从内存存取、缓存概念,并不正确。java的内存模型分为main memory和working memory。注:这里的memory不是真正的内存和cpu缓存,是一个抽象概念。 不想把问题一捅到底,说道硬件这个层面上。在jvm这个层面上说清楚就可以了。具体的jvm实现那是实现的事情。 |
|
| 返回顶楼 | |









