هو قفل قراءة على reentrantreadwritelock كافية للقراءة المتزامنة لعشوائية

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

سؤال

أنا أكتب شيئًا للتعامل مع طلبات القراءة/الكتابة المتزامنة إلى ملف قاعدة البيانات.

reentrantreadwritelock يبدو وكأنه مباراة جيدة. إذا وصلت جميع المواضيع إلى مشاركة RandomAccessFile كائن ، هل أحتاج إلى القلق بشأن مؤشر الملف مع القراء المتزامنين؟ النظر في هذا المثال:

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Database {

    private static final int RECORD_SIZE = 50;
    private static Database instance = null;

    private ReentrantReadWriteLock lock;
    private RandomAccessFile database;

    private Database() {
        lock = new ReentrantReadWriteLock();

        try {
            database = new RandomAccessFile("foo.db", "rwd");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    };

    public static synchronized Database getInstance() {
        if(instance == null) {
            instance = new Database();
        }
        return instance;
    }

    public byte[] getRecord(int n) {
        byte[] data = new byte[RECORD_SIZE];
        try {
            // Begin critical section
            lock.readLock().lock();
            database.seek(RECORD_SIZE*n);
            database.readFully(data);
            lock.readLock().unlock();
            // End critical section
        } catch (IOException e) {
            e.printStackTrace();
        }
        return data;
    }

}

في طريقة getRecord () ، هل التداخل التالي ممكن مع العديد من القراء المتزامنين؟

الموضوع 1 -> getRecord (0)
الموضوع 2 -> getRecord (1)
الموضوع 1 -> يكتسب قفل مشترك
الموضوع 2 -> يكتسب قفل مشترك
الموضوع 1 -> يسعى لتسجيل 0
الموضوع 2 -> يسعى لتسجيل 1
الموضوع 1 -> قراءة السجل في File Pointer (1)
الموضوع 2 -> قراءة السجل في File Pointer (1)

إذا كانت هناك بالفعل مشكلات تزامن محتملة باستخدام reentrantreadwritelock و randomaccessfile ، فما الذي سيكون بديلًا؟

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

المحلول

نعم ، لا يتم مزامنة هذا الرمز بشكل صحيح ، تمامًا كما تخطط. قفل القراءة والكتابة ليس مفيدًا إذا لم يتم الحصول على قفل الكتابة أبدًا ؛ يبدو الأمر كما لو لم يكن هناك قفل.

استخدم التقليدية synchronized حظر لجعل البحث والقراءة يبدو ذريًا لخيوط أخرى ، أو إنشاء مجموعة من RandomAccessFile الحالات التي يتم استعارة للاستخدام الحصري لخيط واحد ثم يتم إرجاعها. (أو ببساطة خصص قناة لكل موضوع ، إذا لم يكن لديك الكثير من المواضيع.)

نصائح أخرى

هذا هو برنامج عينة يتم قفل الملف وفتح الملف.

try { // Get a file channel for the file 

    File file = new File("filename");

    FileChannel channel = new RandomAccessFile(file, "rw").getChannel(); // Use the file channel to create a lock on the file.

    // This method blocks until it can retrieve the lock. 

    FileLock lock = channel.lock(); // Try acquiring the lock without blocking. This method returns // null or throws an exception if the file is already locked. 

    try { 

        lock = channel.tryLock();

    } catch (OverlappingFileLockException e){}


    lock.release(); // Close the file 

    channel.close();
} 

catch (Exception e) { } 

قد ترغب في التفكير في استخدام أقفال نظام الملفات بدلاً من إدارة القفل الخاص بك.

يتصل getChannel().lock() على RandomAccessFile لقفل الملف عبر FileChannel صف دراسي. هذا يمنع الوصول إلى الكتابة ، حتى من العمليات الخارجة عن إرادتك.

بدلاً من ذلك ، قم بتشغيل كائن القفل المفرد بدلاً من الطريقة ، يمكن لـ REENTRANTREADWRITELOCK أن يدعم ما يصل إلى 65535 أقفال كتابة متكررة و 65535 أقفال قراءة.

قم بتعيين قفل القراءة والكتابة

private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();

ثم اعمل عليها ...

أيضًا: أنت لا تلبي استثناءًا وفشلًا في إلغاء تأمين القفل. اتصل بالقفل عند إدخال الطريقة (مثل خزانة Mutex) ، ثم قم بعملك في كتلة Try/Catch مع إلغاء القفل في القسم أخيرًا ، على سبيل المثال:

public String[] allKeys() {
  r.lock();
  try { return m.keySet().toArray(); }
  finally { r.unlock(); }
}

حسنًا ، 8.5 سنوات وقت طويل ، لكنني آمل ألا يكون Necro ...

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

الآن ، من بعيد ، من المنطقي أن يكون لديك ترتيب معين:

  • قفل الملف
  • افتح الجداول
  • افعل أي شيء مع الجداول
  • أغلق الجداول
  • حرر القفل

ومع ذلك ، للسماح بإصدار القفل في جافا ، يجب عدم إغلاق التدفقات! بسبب ذلك تصبح الآلية بأكملها غريبة بعض الشيء (وخطأ؟).

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

  • افتح الجداول
  • قفل الملف
  • افعل أي شيء مع الجداول
  • حرر القفل
  • أغلق الجداول

أظهرت الاختبارات أن هذا لا يعمل. لذلك ، في منتصف الطريق ، قم بالقيام بالباقي بطريقة جيدة

try (RandomAccessFile raf = new RandomAccessFile(filename, "rwd");
    FileChannel channel = raf.getChannel()) {
  FileLock lock = channel.lock();
  FileInputStream in = new FileInputStream(raf.getFD());
  FileOutputStream out = new FileOutputStream(raf.getFD());

  // do all reading
  ...

  // that moved the pointer in the channel to somewhere in the file,
  // therefore reposition it to the beginning:
  channel.position(0);
  // as the new content might be shorter it's a requirement to do this, too:
  channel.truncate(0);

  // do all writing
  ...

  out.flush();
  lock.release();
  in.close();
  out.close();
}

لاحظ أن الأساليب التي تستخدم هذا يجب أن تكون synchronized. وإلا فإن عمليات الإعدام الموازية قد ترمي OverlappingFileLockException عند الاتصال lock().

يرجى مشاركة الخبرات في حال كان لديك أي ...

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