مزامنة جافا 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 على الإطلاق)، ولكن يمكنك تجربة علامة ثابتة بسيطة من نوع ما تتحقق في بداية الطريقة من تشغيلها بالفعل.ثم كل ما عليك فعله هو التأكد من مزامنة أي قراءة/كتابة لهذه العلامة.لذلك، في البداية، تحقق من العلم، إذا لم يتم ضبطه، قم بتعيينه، إذا تم ضبطه، قم بالعودة.إذا لم يتم ضبطها، فقم بتشغيل بقية الطريقة، وبعد اكتمالها، قم بإلغاء ضبطها.فقط تأكد من وضع الكود في محاولة/أخيرًا وإلغاء تحديد العلامة في النهاية بحيث يتم إلغاء ضبطه دائمًا في حالة حدوث خطأ.مبسط للغاية ولكنه قد يكون كل ما تحتاجه.

يحرر:ربما يعمل هذا في الواقع بشكل أفضل من مزامنة الطريقة.لأنك حقا بحاجة إلى ترجمة جديدة في الحال بعد الواحد قبل ما يخلص؟وقد لا ترغب في قفل سلسلة محادثات لفترة طويلة جدًا إذا كان عليها الانتظار لبعض الوقت.

احتفظ بمقبض على خيط التحميل لمعرفة ما إذا كان قيد التشغيل؟

أو ألا يمكنك فقط استخدام علامة متزامنة للإشارة إلى ما إذا كان التحميل قيد التقدم؟

هذا في الواقع مطابق للكود المطلوب لإدارة إنشاء Singleton (اللحظات!) عند القيام به بالطريقة الكلاسيكية:

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

الاختبار الداخلي مطابق للاختبار الخارجي.الاختبار الخارجي هو أننا لا ندخل بشكل روتيني إلى كتلة متزامنة، الاختبار الداخلي هو التأكد من أن الوضع لم يتغير منذ آخر اختبار قمنا به (كان من الممكن استباق الخيط قبل الدخول إلى المزامنة).

في حالتك:

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

تحديث:لن تعمل هذه الطريقة لإنشاء مفردة بشكل موثوق تحت JDK1.4.للتوضيح انظر هنا.ومع ذلك أعتقد أنك ستكون على ما يرام في هذا السيناريو.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top