Ruchira Gayan Ranaweera
Ruchira Gayan Ranaweera

Reputation: 35557

Locking mechanism for object in Java

Let's say customer has a Credit Card. And he has balance of x amount of money and he is buying y valued item(y<x). And again he is going to buy another item witch will cost z. (y+z>x but z<x) .

Now I am going to simulate this scenario in Java. If all transaction happens in sequential, there is no need to panic. Customer can buy y valued item and then he don't have enough credit to buy other one.

But when we come in to multi-threaded environment we have to deal with some locking mechanism or some strategy. Because if some other thread read credit card object before reflect changes by previous thread serious issues will rise.

As far as I can see one way is we can keep a copy of original balance and we can check current value just before update the balance. If value is same as original one then we can make sure other threads doesn't change the balance. If balance different then we have to undo our calculation.

And again Java Synchronization also a good solution. Now my question is what will be the best approach to implement in such a scenario?

Additionally if we are going to see this in big picture. Synchronization hits the performance of the system. Since it is locked the object and other thread has to wait.

Upvotes: 4

Views: 859

Answers (4)

erickson
erickson

Reputation: 269687

What you are talking about sounds like software transactional memory. You optimistically assume that no other threads will modify the data upon which your transaction depends, but you have a mechanism to detect if they have.

The types in the java.util.concurrent.atomic package can help build a lock-free solution. They implement efficient compare-and-swap operations. For example, an AtomicInteger reference would allow you to to something like this:

AtomicInteger balance = new AtomicInteger();

…

void update(int change) throws InsufficientFundsException {
  int original, updated;
  do {
    original = balance.get();
    updated = original + change;
    if (updated < 0)
      throw new InsufficientFundsException();
  } while (!balance.compareAndSet(original, update));
}

As you can see, such an approach is subject to a livelocked thread condition, where other threads continually change the balance, causing one thread to loop forever. In practice, specifics of your application determine how likely a livelock is.

Obviously, this approach is complex and loaded with pitfalls. If you aren't a concurrency expert, it's safer to use a lock to provide atomicity. Locking usually performs well enough if code inside the synchronized block doesn't perform any blocking operations, like I/O. If code in critical sections has a definite execution time, you are probably better off using a lock.

Upvotes: 2

pobrelkey
pobrelkey

Reputation: 5973

As far as I can see one way is we can keep a copy of original balance and we can check current value just before update the balance. If value is same as original one then we can make sure other threads doesn't change the balance. If balance different then we have to undo our calculation.

Sounds like what AtomicInteger.compareAndSet() and AtomicLong.compareAndSet() do.


An easier-to-understand approach would involve using synchronized methods on your CreditCard class that your code would call to update the balance. (Only one synchronized method on an object can execute at any one time.)

In this case, it sounds like you want a public synchronized boolean makePurchase(int cost) method that returns true on success and false on failure. The goal is that no transaction on your object should require more than one method call - as you've realized, you won't want to make two method calls on CreditCard (getBalance() and later setBalance()) to do the transaction, because of potential race conditions.

Upvotes: 0

tokhi
tokhi

Reputation: 21616

I will prefer to have a ReadWriteLock, this helps to to lock it for reading and writing, this is nice because you can have separate read and write lock for each resource:

ReadWriteLock readWriteLock = new ReentrantReadWriteLock();


readWriteLock.readLock().lock();

    // multiple readers can enter this section
    // if not locked for writing, and not writers waiting
    // to lock for writing.

readWriteLock.readLock().unlock();


readWriteLock.writeLock().lock();

    // only one writer can enter this section,
    // and only if no threads are currently reading.

readWriteLock.writeLock().unlock();

ReadWriteLock internally keeps two Lock instances. One guarding read access, and one guarding write access.

Upvotes: 3

Dario
Dario

Reputation: 548

Your proposal doesn't fit. You can't be sure the context switch doesn't happen between check and update.

The only way is synchronization.

Upvotes: 2

Related Questions