Синхронизированный блок вызывает сброс всего кэша записи? - PullRequest
1 голос
/ 18 июня 2019

Меня интересует, как synchronized работает в смысле, как / когда он сбрасывает записи из локальных кэшей. Давайте представим, что у меня есть следующий код:

class Scratch1 {
  int counter = 0;
  Scratch1() throws ExecutionException, InterruptedException {
    counter += 5;
    counter += 5;
    // Does this cause to flush possibly cached value written by main thread even if it locks
    // on totally unrelated object and the write doesnt happen inside the sync block?
    synchronized (String.class) {}
    Executors.newCachedThreadPool().submit(() -> {
      for (int i = 0; i < 1000; i++) {
        counter += 5;
      }
      synchronized (Integer.class) {}
    }).get();
    System.out.println(counter);
  }
}
class Scratch2 {
  int counter = 0;
  Scratch2() throws ExecutionException, InterruptedException {
    // Or is this only possible working way how flush written data.
    synchronized (String.class) {
      counter += 5;
      counter += 5;
    }
    Executors.newCachedThreadPool().submit(() -> {
      synchronized (Integer.class) {
        for (int i = 0; i < 1000; i++) {
          counter += 5;
        }
      }
    }).get();
    System.out.println(counter);
  }
}
class Scratch3 {
  volatile int counter = 0;
  Scratch3() throws ExecutionException, InterruptedException {
    counter += 5;
    counter += 5;
    Executors.newCachedThreadPool().submit(() -> {
      for (int i = 0; i < 1000; i++) {
        counter += 5;
      }
    }).get();
    System.out.println(counter);
  }
}

У меня есть несколько вопросов:

  1. Все ли три примера имеют один и тот же уровень "безопасности потока" (принимая во внимание особенности, такие как первая запись выполняется одним потоком, а вторая запись выполняется после первого (верно?) И другого потока), т. Е. гарантированно напечатано 5010 "?
  2. Есть ли разница в производительности (по крайней мере теоретическая) в «работе» вне синхронизированного блока или работе с энергонезависимыми свойствами (я бы ожидал, что энергозависимый доступ будет медленнее, поскольку этот пост подтверждает ), но в случае синхронизированного блока - это «промывочная» цена, выплачиваемая только при пересечении синхронизированного начала / конца, или есть также разница внутри блока?

1 Ответ

2 голосов
/ 18 июня 2019

Меня интересует, как синхронизируется работает в смысле, как / когда он сбрасывает записи из локальных кэшей.

На самом деле, synchronized не сбрасывает записи из локальных кэшей.Это просто действует так, как если бы это было так.

Все ли три примера имеют один и тот же уровень "безопасности потока" (принимая во внимание особенности, такие как первая запись выполняется одним потоком, а вторая запись выполняется после первойодин (не так ли?) и другой поток), т. е. «гарантировано ли напечатано 10»?

Все они обеспечивают несколько разные формы безопасности потока.Ни один из них не является действительно безопасным, если другие потоки одновременно обращаются к объекту.Например, другой поток, обращающийся к counter, должен будет удерживать блокировки String.class и Integer.class, чтобы гарантировать, что он не видит counter во время операции.Третий использует операции приращения, которые не являются атомарными, хотя это безопасно, если никакой другой поток не пытается изменить counter.

Есть ли разница в производительности (по крайней мере теоретическая) в «работе» внесинхронизированный блок или работа с энергонезависимыми свойствами (я бы ожидал, что энергозависимый доступ будет медленнее, как подтверждает этот пост), но в случае синхронизированного блока это «сбрасываемая» цена, выплачиваемая только при пересечении синхронизированного начала / конца, или есть также разница, когда внутриблок?

без разницы.Ввод блока synchronized требует определенных затрат, поскольку блокировка должна быть получена, а некоторые оптимизации должны быть отключены в точке входа.Выход из блока сопряжен с аналогичными затратами.

Внутри блока нет затрат, потому что программист обеспечивает безопасность, гарантируя, что они не позволяют никакому потоку изменять объект, если он не имеет такой же блокировки и не имеет двухпотоки могут одновременно удерживать одну и ту же блокировку.Вообще говоря, код может даже не знать, находится ли он внутри одного или нескольких блоков synchronized, потому что он может находиться глубоко в дереве вызовов.

...