加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Java:同步操作究竟与波动有关吗?

发布时间:2020-12-14 16:25:50 所属栏目:Java 来源:网络整理
导读:对不起,这是一个很长的问题. 我最近在多线程中进行了大量的研究,因为我慢慢地将其纳入个人项目.然而,可能由于大量略有不正确的示例,在某些情况下使用同步块和波动对我来说仍然不清楚. 我的核心问题是:当线程在同步块内时,对引用和原语的更改会自动发生变化(
对不起,这是一个很长的问题.

我最近在多线程中进行了大量的研究,因为我慢慢地将其纳入个人项目.然而,可能由于大量略有不正确的示例,在某些情况下使用同步块和波动对我来说仍然不清楚.

我的核心问题是:当线程在同步块内时,对引用和原语的更改会自动发生变化(即,在主内存而不是高速缓存上执行),或者读取也必须同步才能工作正常吗?

>如果是,同步一个简单的getter方法的目的是什么? (见示例1)此外,只要线程在任何东西上同步,都将发送到主内存的所有更改?例如,如果它被发送到一个非常高级别的同步处理工作的负载,那么每一次的改变都会变成主内存,没有什么可以缓存,直到它再次解锁?
>如果不是,必须在同步块内显式地进行更改,或者java实际上是否可以使用Lock对象? (见实施例3)
>如果同步对象需要与以任何方式更改的引用/原语相关(例如,包含它的直接对象)?我可以通过在一个对象上同步来写,如果否则安全吗? (见实施例2)

(请注意以下示例,我知道同步方法和同步(这)被皱眉和为什么,但是讨论超出了我的问题的范围)

示例1:

class Counter{
  int count = 0;

  public synchronized void increment(){
    count++;
  }

  public int getCount(){
    return count;
  }
}

在这个例子中,increment()需要同步,因为不是一个原子操作.因此,同时增加的两个线程可能导致整数增加1到计数.计数原语需要是原子的(例如不长/双/引用),这样就好了.

getCount()需要在这里同步吗?为什么?我听到的最多的解释是,我不能保证返回的计数是否是增量之前或之后.然而,这似乎是一些略有不同的解释,那就是发现自己在错误的地方.我的意思是说如果我同步getCount(),那么我仍然看不到保证 – 它现在下来不知道锁定顺序,不知道实际读取是否恰好在实际写入之前/之后.

示例2:

以下示例线程安全,如果您假设通过这里没有显示的诡计,这些方法不会同时被调用?如果每次使用随机方法完成,然后被正确读取,或者锁必须是相同的对象,将以预期的方式计数增量? (btw我完全意识到这个例子是多么可笑,但我对理论比实践更感兴趣)

class Counter{
  private final Object lock1 = new Object();
  private final Object lock2 = new Object();
  private final Object lock3 = new Object();
  int count = 0;

  public void increment1(){
    synchronized(lock1){
      count++;
    }
  }

  public void increment2(){
    synchronized(lock2){
      count++;
    }
  }

  public int getCount(){
    synchronized(lock3){
      return count;
    }
  }

}

示例3:

事情之前的关系是简单的java概念,还是JVM中的一个实际的东西?即使我可以保证一个概念性的发生 – 在下一个例子的关系之前,java是否足够聪明,如果它内置的东西呢?我假设不是,但这个例子其实是线程安全的?如果它的线程安全,那么如果getCount()没有锁定呢?

class Counter{
  private final Lock lock = new Lock();
  int count = 0;

  public void increment(){
    lock.lock();
    count++;
    lock.unlock();
  }

  public int getCount(){
    lock.lock();
    int count = this.count;
    lock.unlock();
    return count;
  }
}

解决方法

是的,读取也必须同步. This page说:

The results of a write by one thread are guaranteed to be visible to a
read by another thread only if the write operation happens-before the
read operation.

[…]

An unlock (synchronized block or method exit) of a monitor
happens-before every subsequent lock (synchronized block or method
entry) of that same monitor

同一页说:

Actions prior to “releasing” synchronizer methods such as Lock.unlock,
Semaphore.release,and CountDownLatch.countDown happen-before actions
subsequent to a successful “acquiring” method such as Lock.lock

所以锁提供与同步块相同的可见性保证.

无论使用同步块还是锁,只有读写器线程与写入线程使用相同的监视器或锁时,才能保证可见性.

>您的示例1不正确:如果要查看计数的最新值,则getter也必须同步.>您的示例2是不正确的,因为它使用不同的锁来保护相同的计数.>你的例子3可以.如果吸气剂没有锁定,您可以看到较旧的计数值.事情之前是JVM保证的. JVM必须遵守指定的规则,例如通过将缓存刷新到主内存.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读