【问题标题】:Effect of java synchronized monitor enter on non synchronized access or not volatile variablesjava同步监视器输入对非同步访问或非易失性变量的影响
【发布时间】:2020-09-11 04:43:39
【问题描述】:

我有一个下载操作代码,看起来像这样

while(true){
  if(target.flagStop){
    break;
  }else{
    x=target.check();
  }
len=in.read(buff,0,min(BUFFER_SIZE,x));
  out.write(buff,0,len);
  target.position+=len;
}

其中flagStop 是一个易失性布尔值 position 是一个 non volatile long 值 在check() 方法中,我有一个synchronized

long check(){
  //some code here
  synchronized(aLock){
  //some code here
    return something;
  }
}

我在这个线程中更新(写访问)positiononly(只关心这个是否有最新的更新)但也会发生来自不同线程的一些读取,我的情况只是用于监控目的 所以比我预期的要低几个字节,这与声明为 volatile 的值相比无关紧要,这会影响我的主要目的的性能

我知道 CPU 指令完成后,数据会在计算进度结果返回内存后到达 CPU 寄存器 哪个
- 如果该变量声明为volatile,则结果将立即写入主内存(不再缓存)
- 否则这将被存储在线程缓存内存中,之后这个值将被写入主内存(从缓存写入主内存,时间无法确定(这可以立即或延迟无人知道)我的情况我的问题是关于这种情况的价值 不是 volatileonly 在一个线程中

根据 StackOverflow 中一位亲爱的用户在 here 中的回答,当我们首先输入 synchronized 块时
(案例 1):我们有一个来自主内存的读操作(称为读屏障)

synchronized 块的末尾
(案例 2):我们对主内存进行了写操作(称为写屏障)

我知道 案例 2
所有修改过的线程缓存变量都会写入主存 但我可能想错了,在 案例 1: 我们有一个来自主内存的读取操作,它使用存储在主内存中的版本覆盖线程的缓存。(main -> cache)

正如我之前提到的,我的 position 值是 not volatile(所以没有直接访问读/写到主内存使用缓存值),如果我输入 synchronized case 1 发生的块(因为可能,来自线程缓存的较新位置值还没有机会将其值写入主内存) 并将位置的主内存(可能是旧的)版本覆盖到线程缓存中(即通过覆盖通过同步monitor enter 操作检索到的旧值来销毁新的位置)
我真的这么想吗?
我是否必须将position 声明为volatile
如果我错了,告诉我monitor enter(或我之前提到的 case 1 处的线程缓存中发生的事情) 提前感谢您的指导。

【问题讨论】:

  • I also update(write access) position only in this thread 是在其他任何地方访问的位置,包括读取?
  • after compution progress result will come back to memory 嗯,不是真的。将结果保存在寄存器中并且写回主内存是完全有效的。 variable declared as volatile the result immediately will be written“使可见”与“写入主存”不同。可能是这样,但认为这种情况总是发生可能是危险的。 otherwise this will be stored in thread cache memory 可以被存储,但也不是 100% 保证的。东西往往不会被随机缓存,只是在你最意想不到的时候逃到野外。
  • 你链接到的那个解释不是很好。规范实际上说的是在锁释放的结束 和锁获取的开始 之间存在happens-before 关系。
  • since that possible ,newer position value from thread's cache have not yet have chance to writes its value to main memory 我不确定你在这里想说什么,但我认为答案是否定的,没有什么不好的事情发生。这就是试图说锁获取会创建读取障碍的麻烦,但事实并非如此。去阅读规范。它说单个线程访问的变量是线程安全的,句号。没有 happens-before 可以改变这一点。只要只有一个线程看到position 就可以了。
  • @markspace is position accessed anywhere else, including reads? 是的,因为这个变量的主要目的是在这个线程中,并从其他线程读取只是为了监控 porposes,在这种情况下,我对几个字节或多或少不敏感跨度>

标签: java multithreading concurrency synchronization volatile


【解决方案1】:

您正在寻找的部分内容:

https://docs.oracle.com/javase/specs/jls/se14/html/jls-17.html#jls-17.4.1

可以在线程之间共享的内存称为共享内存或 堆内存。

所有实例字段、静态字段和数组元素都存储在 堆内存。在本章中,我们使用术语变量来指代 字段和数组元素。

局部变量(第 14.4 节)、形式方法参数(第 8.4.1 节)和 异常处理程序参数(第 14.20 节)永远不会在线程之间共享 并且不受内存模型的影响。

这里的“不受影响”表示它们不需要同步。只要只有一个线程看到一个变量,它总是没问题的。

这也有帮助:

https://docs.oracle.com/javase/specs/jls/se14/html/jls-17.html#jls-17.4.7

执行遵循线程内一致性。

对于每个线程 t,t 在 A [actions by that thread] 中执行的操作与 将由该线程以程序顺序单独生成,其中 每次写入 w 写入值 V(w),假设每次读取 r 看到 值 V(W(r))。每次读取看到的值由内存决定 模型。给出的程序顺序必须反映其中的程序顺序 动作将根据线程内语义执行 P.

操作意味着读取和写入。所以你的变量position 不允许因为同步而更新一些奇怪的值。单个执行线程中的读取和写入按照程序语句指定的顺序发生。系统不会将奇怪的读取或写入乱序拉出缓存或主内存。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-11
    • 1970-01-01
    相关资源
    最近更新 更多