سؤال

suppose we have these classes:

public  class Record {
    int key;
    int value;
    Record(){
        this.key=0;
        this.value=0;
    }
    Record(int key,int value){
        this.key=key;
        this.value=value;
    }
    public  class Table {
        static final Record[] table = new Record [100];
        static final Object[] locks = new Object[table.length];
        static{
        for(int i = 0; i < table.length; i++) {
            locks[i] = new Object();
        }


        table[0]=new Record(0,0);
        table[1]=new Record(1,10); 
        table[2]=new Record(2,20);
        table[3]=new Record(3,30);
    }
}

And i want to implement in TRansaction class these method

void setValueByID(int key, int value)

The key-value pair (record) is locked(it cannot be read/written from other transactions) until the setValueByID method finishes.

int getValueByID(int key)

The key-value pair (record) is locked until the transaction commits

void commit()

it unlocks all the key-value pairs (records) locked in the current transaction

So, my implementation is :

class Transaction extends Thread {
//there is no problem here  
    public void setValueByID(int key, int value){

    synchronized(Table.locks[key]) {
     Table.table[key].key=key;

    }   
    }
    //the problem is here...
    //how can i make other thread wait until current thread calls Commit()
    public int getValueByID(int key){
            int value=0; 
            synchronized(Table.locks[key]){
            value= Table.table[key].key;
     }
     return value;
} 


void commit(){
}

Ahmad

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

المحلول

You cannot use synchronized blocks to achieve it, instead you will need to use something like locks.

public Main {
    public static void main(String[] args) {
        final Transaction T = new Transaction();
        for (int n = 0; n < 10; n++) {
            new Thread(new Runnable() {
                public void run() {
                    for (int i = 0; i < 1000; i++) {
                        T.setValueByID(i % 100, i);
                        T.getValueByID(i % 100);
                        if (i % 20 == 0) T.commit();
                    }
                }  
            }).start();
        }
    }
}
class Table {
    static final Record[] table = new Record[100];
    static final ReentrantLock[] locks = new ReentrantLock[table.length];

    static {
        for (int i = 0; i < table.length; i++) {
            locks[i] = new ReentrantLock();
        }

        table[0] = new Record(0, 0);
        table[1] = new Record(1, 10);
        table[2] = new Record(2, 20);
        table[3] = new Record(3, 30);
    }
}
class Transaction {

    private ThreadLocal<Set<ReentrantLock>> locks = new ThreadLocal<Set<ReentrantLock>>() {
        @Override
        protected Set<ReentrantLock> initialValue() {
            return new HashSet<ReentrantLock>();
        }
    };

    private void attainLock(int key) {
        final ReentrantLock lock = Table.locks[key];
        lock.lock();
        locks.get().add(lock);
    }

    private void releaseLock(int key) {
        final ReentrantLock lock = Table.locks[key];
        releaseLock(lock);
    }

    private void releaseLock(ReentrantLock lock) {
        final Set<ReentrantLock> lockSet = locks.get();
        if (!lockSet.contains(lock)) {
            throw new IllegalStateException("");
        }
        lockSet.remove(lock);
        lock.unlock();
    }

    private void releaseLocks() {
        final Set<ReentrantLock> lockSet = new HashSet<ReentrantLock>(locks.get());
        for (ReentrantLock reentrantLock : lockSet) {
            releaseLock(reentrantLock);
        }
    }

    public void setValueByID(int key, int value) {
        attainLock(key);
        Table.table[key].key = key;
        releaseLock(key);
    }

    public int getValueByID(int key) {
        attainLock(key);
        return Table.table[key].key;
    }

    void commit() {
        releaseLocks();
    }
}

The problem with locks is that during your transaction, if you do not follow an order while attaining the locks, you can run into deadlocks! Also, you need to ensure you handle exceptions properly and always release the locks by calling commit().

نصائح أخرى

This sort of synchronization is achieved using the wait()/notify() methods with some sort of lock. Read the Java tutorial on them (and other synchronization concepts) here.

Ideally you need to become familiar with the ReadWriteLock of java concurrent package. setValueId should obtain a write lock and hold it until transaction commits. getValueId only needs a read lock that can be shared by other read locks and be released when the method ends.

Also take into account some timeout so that the locks are not held indefinitely.

Research for:

  • read/write lock pattern
  • isolation levels in database systems
  • transaction handling
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top