代码之家  ›  专栏  ›  技术社区  ›  Ihor M.

Java同步块和显式锁的可见性保证

  •  2
  • Ihor M.  · 技术社区  · 3 年前

    我有一个类,其中有一个由几个字段集合组成的状态。

    现在,我有一些方法可以改变这种状态。还有一些方法在阅读它。

    我想使用显式锁来保护状态更改修改。

    class Sample {
      private final Map<String> a = new HashMap<>();
      private final Map<String> b = new HashMap<>();
      private final Map<String> c = new HashMap<>();
    
      // protects the state of variables a,b,c so that the state modifications are atomic
      private final Object lock = new Object();
    
      ...
      // other fields
    
      private void changeState(StateChange obj){
         synchronized(lock){
             changeA();
             changeB();
             changeC();
        }
      }
    
      private State readState(){
         synchronized(lock){
             return buildStateFromABC();
        }
      }
    
    }
    

    如果这个类是在多线程环境中访问/修改的,那么每个线程在进入保护区后会看到字段a、b、c的最新内容吗?

    锁和这些字段之间没有直接的相关性,或者有什么关系吗?

    我知道我可以在这里使用一个内部锁,只需对方法进行同步处理,但我想尽量减少受保护的区域。

    在这里同步的首选方式是什么?在显式锁定对象上或在 this ?

    1 回复  |  直到 3 年前
        1
  •  0
  •   Eugene    3 年前

    当有疑问时,有 JLS , see here :

    显示器上的解锁 以前发生过 每次后续锁定 那个 班长

    所以你的方法 readState changeState (因为他们使用 相同的 锁定),将创建 以前发生过 边缘,需要适当的可见性。

    中也有此部分 JLS 上面写着:

    如果一个程序被正确同步,那么该程序的所有执行将看起来是顺序一致的(§17.4.3)。

    然后:

    这对程序员来说是一个非常有力的保证。程序员不需要对重新排序进行推理来确定他们的代码是否包含数据竞赛。因此,在确定代码是否正确同步时,他们不需要对重新排序进行推理。一旦确定代码是正确同步的,程序员就不需要担心重新排序会影响他或她的代码。

    因此,如果您了解在连接发生之前是如何发生的(根据JLS保证),那么您的代码是线程安全的。


    至于赞成哪一个,a synchronized 方法或显式锁(我仍然建议 ReentrantLock 在您的情况下),可能是 this read 可以帮上一点忙。