سؤال

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

  • أريد الوصول المتزامن إلى قائمة الانتظار، ودفع المنتج، وظهور جهاز الاستقبال.
  • عندما تكون قائمة الانتظار فارغة، أريد من المستهلك أن يحظر الدخول إلى قائمة الانتظار، في انتظار المنتج.

كانت فكرتي الأولى هي استخدام LinkedBlockingQueue, لكن سرعان ما أدركت أنه غير متزامن وتأثر الأداء.ومن ناحية أخرى، أستخدم الآن ConcurrentLinkedQueue, ، ولكن ما زلت أدفع التكلفة wait() / notify() على كل منشور.نظرًا لأن المستهلك، عند العثور على قائمة انتظار فارغة، لا يحظر، فلا بد لي من المزامنة و wait() على القفل.من ناحية أخرى، يجب على المنتج الحصول على هذا القفل و notify() عند كل منشور.والنتيجة الإجمالية هي أنني أدفع التكلفةsycnhronized (lock) {lock.notify()} في كل منشور، حتى عندما لا تكون هناك حاجة إليه.

أعتقد أن ما نحتاجه هنا هو قائمة انتظار محظورة ومتزامنة.أتخيل أ push() عملية للعمل كما في ConcurrentLinkedQueue, ، مع اضافية notify() إلى الكائن عندما يكون العنصر المدفوع هو الأول في القائمة.مثل هذا الشيك أعتبره موجودًا بالفعل في ConcurrentLinkedQueue, لأن الدفع يتطلب الاتصال بالعنصر التالي.وبالتالي، سيكون هذا أسرع بكثير من المزامنة في كل مرة على القفل الخارجي.

هل شيء من هذا القبيل متاح/معقول؟

هل كانت مفيدة؟

المحلول

أعتقد أنه يمكنك التمسك بها java.util.concurrent.LinkedBlockingQueue بغض النظر عن شكوكك.إنه متزامن.على الرغم من ذلك، ليس لدي أي فكرة عن أدائها.ربما، تنفيذ أخرى ل BlockingQueue سوف يناسبك بشكل أفضل.ليس هناك الكثير منها، لذا قم بإجراء اختبارات الأداء والقياس.

نصائح أخرى

مشابه لهذه الإجابة https://stackoverflow.com/a/1212515/1102730 ولكن مختلفة بعض الشيء ..انتهى بي الأمر باستخدام ExecutorService.يمكنك إنشاء مثيل باستخدام Executors.newSingleThreadExecutor().كنت بحاجة إلى قائمة انتظار متزامنة لقراءة/كتابة BufferedImages في الملفات، بالإضافة إلى الذرية مع عمليات القراءة والكتابة.أحتاج فقط إلى مؤشر ترابط واحد لأن ملف الإدخال/الإخراج أسرع من المصدر، صافي الإدخال/الإخراج.أيضًا، كنت مهتمًا بذرية الإجراءات وصحتها أكثر من الأداء، ولكن يمكن أيضًا تنفيذ هذا النهج باستخدام سلاسل رسائل متعددة في المجموعة لتسريع الأمور.

للحصول على صورة (تم حذف محاولة الالتقاط أخيرًا):

Future<BufferedImage> futureImage = executorService.submit(new Callable<BufferedImage>() {
    @Override
        public BufferedImage call() throws Exception {
            ImageInputStream is = new FileImageInputStream(file);
            return  ImageIO.read(is);
        }
    })

image = futureImage.get();

لحفظ صورة (تم حذف محاولة الالتقاط أخيرًا):

Future<Boolean> futureWrite = executorService.submit(new Callable<Boolean>() {
    @Override
    public Boolean call() {
        FileOutputStream os = new FileOutputStream(file); 
        return ImageIO.write(image, getFileFormat(), os);  
    }
});

boolean wasWritten = futureWrite.get();

من المهم ملاحظة أنه يجب عليك مسح التدفقات وإغلاقها في كتلة نهائية.لا أعرف كيفية أدائه مقارنة بالحلول الأخرى، ولكنه متعدد الاستخدامات إلى حد كبير.

أود أن أقترح عليك أن تنظر ThreadPoolExecutor newSingleThreadExecutor.وسوف يتعامل مع الحفاظ على المهام الخاصة بك مرتبة لك، وإذا قمت بتقديمها قابلة للاستدعاء إلى منفذك، سوف تكون قادرًا على الحصول على سلوك الحظر الذي تبحث عنه أيضًا.

يمكنك تجربة LinkedTransferQueue من jsr166: http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/

إنه يلبي متطلباتك ويتحمل تكاليف أقل لعمليات العرض/الاستفتاء.كما أرى من الكود، عندما لا تكون قائمة الانتظار فارغة، فإنها تستخدم العمليات الذرية لاستقصاء العناصر.وعندما تكون قائمة الانتظار فارغة، فإنها تدور لبعض الوقت وتتوقف الخيط إذا لم ينجح.أعتقد أنه يمكن أن يساعد في حالتك.

أستخدم ArrayBlockingQueue عندما أحتاج إلى تمرير البيانات من موضوع إلى آخر.استخدام أساليب الوضع والأخذ (والتي سيتم حظرها إذا كانت ممتلئة/فارغة).

هنا أ قائمة الطبقات المنفذة BlockingQueue.

أود أن أوصي بالخروج SynchronousQueue.

كما ذكرRorick في تعليقه، أعتقد أن كل هذه التطبيقات متزامنة.أعتقد أن مخاوفك مع LinkedBlockingQueue قد يكون في غير مكانه.

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