Синхронизация Java 1.4:разрешить запуск только одного экземпляра метода (неблокирующего)?

StackOverflow https://stackoverflow.com/questions/152138

Вопрос

У меня есть класс, предлагающий утилиты для перевода.Сами переводы должны перезагружаться каждые 30 минут.Для этого я использую поддержку Spring Timer.В принципе, мой класс выглядит так:

public interface Translator {
    public void loadTranslations();
    public String getTranslation(String key);
}

loadTranslations() может выполняться довольно долго, поэтому пока он работает, старые переводы все еще доступны.Это делается путем загрузки переводов на локальную карту и простого изменения ссылки после загрузки всех переводов.

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

Синхронизированный метод будет ставить в очередь только загрузки...Я все еще использую Java 1.4, поэтому нет java.util.concurrent.

Спасибо за вашу помощь !

Это было полезно?

Решение

Используйте какой-либо механизм блокировки, чтобы выполнять задачу только в том случае, если она еще не выполняется.Получение токена блокировки должно быть одноэтапным процессом.Видеть:

/**
 * @author McDowell
 */
public abstract class NonconcurrentTask implements Runnable {

    private boolean token = true;

    private synchronized boolean acquire() {
        boolean ret = token;
        token = false;
        return ret;
    }

    private synchronized void release() {
        token = true;
    }

    public final void run() {
        if (acquire()) {
            try {
                doTask();
            } finally {
                release();
            }
        }
    }

    protected abstract void doTask();

}

Тестовый код, который выдаст исключение, если задача выполняется одновременно:

public class Test {

    public static void main(String[] args) {
        final NonconcurrentTask shared = new NonconcurrentTask() {
            private boolean working = false;

            protected void doTask() {
                System.out.println("Working: "
                        + Thread.currentThread().getName());
                if (working) {
                    throw new IllegalStateException();
                }
                working = true;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                if (!working) {
                    throw new IllegalStateException();
                }
                working = false;
            }
        };

        Runnable taskWrapper = new Runnable() {
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    shared.run();
                }
            }
        };
        for (int i = 0; i < 100; i++) {
            new Thread(taskWrapper).start();
        }
    }

}

Другие советы

Я из .net (вообще не имею опыта работы с Java), но вы можете попробовать какой-нибудь простой статический флаг, который в начале метода проверяет, работает ли он уже.Тогда все, что вам нужно сделать, это убедиться, что все операции чтения/записи этого флага синхронизированы.Итак, вначале проверьте флаг, если он не установлен, установите его, если установлен, вернитесь.Если он не установлен, запустите оставшуюся часть метода и после его завершения отключите его.Просто не забудьте поместить код в try/finally и установить флаг iunsetting в наконец, чтобы он всегда сбрасывался в случае ошибки.Очень упрощенно, но может быть все, что вам нужно.

Редактировать:На самом деле это, вероятно, работает лучше, чем синхронизация метода.Потому что вам действительно нужен новый перевод? немедленно после того, что было до того, как оно закончилось?И, возможно, вам не захочется блокировать поток слишком надолго, если ему придется подождать некоторое время.

Держите дескриптор потока загрузки, чтобы проверить, работает ли он?

Или вы не можете просто использовать флаг синхронизации, чтобы указать, выполняется ли загрузка?

На самом деле это идентично коду, который необходим для управления созданием синглтона (ах!), если выполнять его классическим способом:

if (instance == null) {
  synchronized {
    if (instance == null) {
       instance = new SomeClass();
    }
  }
}

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

В твоем случае:

if (translationsNeedLoading()) {
  synchronized {
    if (translationsNeedLoading()) {
       loadTranslations();
    }
  }
}

ОБНОВЛЯТЬ:Этот способ создания синглтона не будет надежно работать под вашим JDK1.4.Для объяснения глянь сюда.Однако я думаю, что с вами все будет в порядке в этом сценарии.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top