Когда использовать синхронизацию в Java

Надеюсь, этого будет достаточно информации, так что вот оно. Если вам нужна дополнительная информация, lemme знает в комментариях.

У меня есть класс, который имеет два внутренних класса. У внутренних классов есть два метода, которые вызывают метод во внешнем классе. Итак, это выглядит так:

public OuterClass {
    private boolean outerMethodHasBeenCalled = false;

    private void outerMethod() {
        if(!outerMethodHasBeenCalled) {
            // do stuff
        }

        outerMethodHasBeenCalled = true;
    }

    private FirstInnerClass {
        public void someMethod() {
            outerMethod();
        }
    }

    private SecondInnerClass {
        public void someOtherMethod() {
            outerMethod();
        }
    }
}

Важно отметить, что:

  • Это приложение для Android. Экземпляры FirstInnerClass и SecondInnerClass передаются в WebView как интерфейс JavaScript, поэтому someMethod и someOtherMethod могут быть вызваны в любой момент, в определенном порядке.
  • В настоящее время у меня проблема с существующим кодом (без ключевого слова synchronized), где outerMethod вызывается в одно и то же время (я распечатываю сообщение журнала, и они привязаны к 1000-й секунде ) разными объектами. Мое приложение затем "делает вещи" дважды, потому что outerMethodHasBeenCalled остается ложным при вызове outerMethod. Это не нормально, и это именно то, что я пытаюсь предотвратить. Мое приложение должно только "делать вещи" один раз и только один раз: первый раз outerMethod вызывается.
  • Может показаться, что у меня есть несколько экземпляров OuterClass, но будьте уверены, что это только один экземпляр OuterClass.

Важно, чтобы мое приложение "делало вещи" только в первый раз outerMethod получило название (я надеюсь, что это очевидно сейчас). Все последующие вызовы по существу игнорируются. Какой бы внутренний класс не называл outerMethod первым - не имеет значения.

Итак, целесообразно ли использовать в этом случае синхронизированное ключевое слово?

Ответ 1

Да, с учетом того, что вы изложили выше, я бы пошел с:

private synchronized void outerMethod() {
...
}

Обратите внимание, что это будет иметь побочный эффект блокировки одного из вызывающих абонентов до завершения функции outerMethod(). Если это приемлемо, остынь. Если целью является только то, что код в outerMethod() запускается один раз, и это нормально, если второй вызывающий абонент не задерживается, если первый вызывающий абонент запускает внешний метод(), вы можете подумать:

public OuterClass {
    private AtomicBoolean outerMethodHasBeenCalled = new AtomicBoolean();

    private void outerMethod() {
        if (outerMethodHasBeenCalled.compareAndSet(false, true)) {
            // do stuff
        }
    }
...

Смотрите JavaDoc для AtomicBoolean, чтобы узнать, что там происходит (если он доступен в Android Java).

Ответ 2

Оберните все в outerMethod, которое вы хотите запускать только один раз в синхронизированном блоке:

private void outerMethod() {
    synchronized (this) {
        if(!outerMethodHasBeenCalled) {
            // do stuff
        }

        outerMethodHasBeenCalled = true;
    }
}

Таким образом, при первом вызове метода только один поток будет включен в синхронизированный блок за раз. Первый выполнит код в инструкции if, а затем установите outerMethodHasBeenCalled в true. Другие потоки поймут, что это правда, и пропустите код if.