java原语ints是设计的还是偶然的?

就这点而言,java原始整数(int)是原子的吗?对共享int的两个线程的一些实验似乎表明它们,但当然,没有证据表明它们不是并不意味着它们是

具体来说,我进行的测试是:

公共类IntSafeChecker{
静态整数事物;
静态布尔值keepWatching=true;
//Watcher只是寻找单调递增的值
静态类观察程序扩展线程{
公开募捐{
布尔hasBefore=false;
int thingBefore=0;
同时(继续观察){
//观察共享int
int thingNow=事物;
//伪造第一个值以使测试满意
if(hasBefore==false){
thingBefore=thingNow;
hasBefore=真;
}
//检查是否减少(由于部分写入值)
if(thingNow<thingBefore){
System.err.println(“主要故障!”);
}
thingBefore=thingNow;
}
}
}
//修饰符只计算10亿的共享整数
静态类修饰符扩展线程{
公开募捐{
int what=0;
对于(整数i=0;i<100000000;++i){
什么+=1;
事物=什么;
}
//完成后杀死观察者
keepWatching=false;
}
}
公共静态void main(字符串[]args){
修饰符m=新修饰符();
观察者w=新观察者();
m、 start();
w、 start();
}
}

(这仅在32位windows PC上使用java jre 1.6.0_07进行了尝试)

本质上,修饰符将一个计数序列写入共享整数,同时观察者检查观察值是否从未减少。在一台32位值必须作为四个独立字节(甚至两个16位字)访问的机器上,观察者很可能会捕获处于不一致、半更新状态的共享整数,并检测到值的减少而不是增加。无论(假设的)数据字节是收集/写入LSB 1st还是写入MSB 1st,这都应该有效,但充其量只是概率性的

考虑到当今广泛的数据路径,32位值很可能是有效的原子值,即使java规范不需要它。事实上,使用32位数据总线,您可能需要比使用32位整数更努力地获得对字节的原子访问

谷歌搜索“java原语线程安全”会发现线程安全类和对象上的大量内容,但寻找原语信息似乎是大海捞针

默认情况下,Java中的所有内存访问都是原子的,除了longdouble(它们可能是原子的,但不一定是原子的)。老实说,这不是很清楚,但我相信这就是其中的含义

根据JLS第17.4.3节:

在顺序一致的
执行时,有一个总订单已结束
所有单个操作(如读取
并写入)这与
程序的顺序,以及每个
个人行动是原子的,是独立的
每根线都能立即看到

然后在17.7中:

一些实现可能会找到它
很方便地分割一次写入
对64位长或双精度文件执行操作
将值转换为上的两个写入操作
相邻的32位值。对于
为了提高效率,这种行为是错误的
具体实施;Java虚拟
机器可以自由地执行写入操作
长值和双值原子或
分为两部分

注意原子性和波动性是非常不同的

当一个线程将一个整数更新为5时,可以保证另一个线程不会看到1、4或任何其他中间状态,但如果没有任何显式的波动性或锁定,另一个线程可能永远看到0

关于努力获得字节的原子访问权,你是对的:虚拟机可能需要努力。。。但这是必须的。根据规范第17.6节:

某些处理器不提供
能够写入单个字节。信息技术
实现字节是非法的
在这样的处理器上通过
简单地读一个单词,
更新适当的字节,以及
然后把整个单词写回
记忆力这个问题有时很难解决
被称为单词撕裂,等等
无法轻松更新数据的处理器
孤立的单字节
这需要一种方法

换句话说,这取决于JVM是否正确

发表评论