У меня есть несколько потоков, обращающихся к class Aufzahlen и увеличивающих переменную cc. Интересно, не ставлю ли я cc на volatile, но мой метод на synchronized, есть ли здесь место для ошибки? Поскольку переменная cc будет доступна только один раз за раз.

Могут ли потоки по-прежнему иметь переменную cc в своем собственном кеше и, следовательно, возиться с ней? Я уже некоторое время запускал этот пример кода и пока не обнаружил никаких ошибок.

public class Aufzahlen {
private int cc=1;

  public synchronized void aufzahlen() throws InterruptedException {
    cc = cc +1;
  }


  public synchronized void printa() {
    // TODO Auto-generated method stub
    System.out.println("Thread: " + Thread.currentThread() + "Zahl: " + cc);
  }

{
4
Timo N. 27 Дек 2015 в 11:52

3 ответа

Лучший ответ

1. Заметность

Этот метод не подведет. Если вы синхронизируете каждый раз, когда получаете доступ к полю, изменения в нем гарантированно будут видны каждый раз, когда поток удерживает ту же блокировку, как показано ниже:

enter image description here

Из практики параллелизма Java [pdf], рисунок 3.1

Таким образом, нет необходимости делать поле нестабильным. Вероятно, вам будет намного лучше, если использовать AtomicInteger rel = "nofollow noreferrer"> getAndIncrement() и incrementAndGet(), которые служат той же цели, но с большей пропускной способностью параллелизма (они используют встроенные функции вместо блокировки, они были разработаны именно для эту задачу) - но убедитесь, что видимость памяти указан сам AtomicInteger (самый простой способ сделать это - сделать его окончательным - это не сделает окончательное значение AtomicInteger).Can't load full resultsTry againRetrying...Retrying...

public class Aufzahlen {
    private final AtomicInteger atomicInt = new AtomicInteger(1); // set initial value with constructor argument

    public void aufzahlen() {
        atomicInt.incrementAndGet();
    } // no need for synchronization because updates are atomic and are automatically visible
}

2. Атомарность

Я мог бы использовать «атомарный» один или два раза в этом посте, это означает, что никакие два потока не могут одновременно выполнять один и тот же код, или, другими словами, операцию нельзя «разделить» на несколько частей, что несколько потоков могут быть внутри одновременно.

+--- Function aufzählen() without synchronization and with i++ ---------------+
|                                                                             |
| [Get i from field] --> [Increment i locally] --> [Write new value to field] |
|                     ^                         ^                             |
|                     |                         |                             |
|            "Race condition", multiple threads could be at these stages      |
|            -> operation not atomic                                          |
+-----------------------------------------------------------------------------+

Я только что продемонстрировал, что не является атомарным, вы можете сделать эту операцию атомарной, используя synchronized (следующий поток должен дождаться, пока первый завершит весь метод), или вы можете использовать AtomicInteger, который делает эту атомарность за вас.

7
RAnders00 29 Дек 2015 в 00:36

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

0
Prim 27 Дек 2015 в 09:13

Переменные volatile указывают компилятору никогда не кэшировать свое значение в регистре как при чтении, так и при записи. Кеши процессора (L1, L2 и т. Д.) По-прежнему используются.

Если все обращения к этой переменной (в одном экземпляре класса) происходят из нескольких потоков, все они должны быть защищены.

Если вам интересно прочитать это значение cc из другого потока без синхронизации, тогда да, сделайте это volatile. Это фактически сделает синхронизацию блокировкой записи.

0
egur 27 Дек 2015 в 09:13