How to ask for lock on data of a datastructure in java

Viewed 80

Recently, while studying transactions in modern databases, I found out that nowadays transactions don't use a lock or monitor on an entire collection or table, but they usually do it on data of the collection or table that is going to be used by different operations the transactions do.

So I was thinking, let's say we have a data structure, it could be a LinkedList, an Hashtable etc, and multiple different transactions want to access the structure concurrently to update data. How can I ask for lock on the data that is going to be used by the transaction rather then on the entire object data structure? This would obviously improve performances because different update transactions that use different data of the same data structure will happen concurrently.

I will try to clarify more what I would like to achieve, here is an example:

public class Table {

    // suppose this is the data of the table and it has a lot of values
    private LinkedList<String> data = new LinkedList();

    public void doTransactionJob() {

        // here we get the data from the list
        // and we ask for monitor on this data so that no other
        // transaction can operate on it
        synchronized(data.get(randomIndex)) {
            // here the transaction works on the data but doesnt
            // block any other transaction from working on the same table
            // but with different data
        }
    }

}

Does something similar to my example above exists?

2 Answers

You can use a SynchronizedList object, it will block and sync any operation on the list, even reading operations, so it's better use when adding or updating the list, or a CopyOnWriteList that will create ever a new list with the new objects, and will not cause problems to a reading operation that would be running. But if your problem is with the Object itself inside a position on the list, I recommend using a ReentrantReadWriteLock strategy on the methods you want to block, so you can manage better when lock, only when people update the object or something, or even using a StampedLock.

Here you can see how use Java locks. What you say with "lock the data the transaction is using" is very vague, you can't block an entire object AFAIK, you can block pieces of that object, that only one thread can pass through at time. Anyway if you don't want to use locks on the class methods, you can create a second list, a list of object ID's that are been used, and can't be get by other transaction, you can combine this with a lamport bakery algorithm, or a simple code that add transactions that want that object to a queue;

List<Object> objects = new ArrayList<>();
List<Integer> objectIds = new ArrayList<>();
Queue<Integer> transactionIds = new PriorityQueue<>();
// code ...
// suppose this is a transaction that run on a thread, and can access our lists
public void doUpdateOnObject() {
    Object object = objects.get(1);
    if (objectsIds.indexOf(object) > 0) {
        transactionIds.add(this.id) // this transactionId
        while(transactionIds.peek() != this.id) { // if you aren't the next, wait    
            Thread.sleep(400);
        }
    }
    objectIds.add(object.getId());
    // here change object fields or whatever;
    objectIds.remove(object.getId());
}

Actually your example works fine, because it use the object that you want to be locked as monitor, so no other threads can access it while one thread is inside the synchronized block.

After a lot of reasoning behind this problem me, @Kaneda and @user207421 came up with a solution which is:

public class Table {

    // suppose this is the data of the table and it has a lot of values
    private LinkedList<String> data = new LinkedList();

    public void doTransactionJob() {

        // here we get the data from the list
        // and we ask for monitor on this data so that no other
        // transaction can operate on it
        synchronized(data.get(randomIndex)) {
            // here the transaction works on the data but doesnt
            // block any other transaction from working on the same table
            // but with different data
        }
    }

}

This works because of the comment @Kaneda wrote which states

It returns the value of the object reference, if a get operation on a DS give only the value, you would never be able to update the object state in a list, and need to set it in that position the new object value again.

So appearently getting the value from the list and asking for monitor on it works completely fine.

Related