Search code examples
javaconcurrencylocking

Java: best way to share Lock between different threads


Is it advisable to implement a extrinsic lock as an attrubite of a class that will be shared between different threads?

For example I would like to have a implementation like this:

public class Order {

    private final LinkedList<Food> order;
    private final int orderNum;
    private static int idGen;
    private final Lock lock;
    private final Condition isDone;

    public Order() {
        // TODO Auto-generated constructor stub
        this.order = new LinkedList<>();
        this.orderNum = ++idGen;
        this.lock =  new ReentrantLock();
        this.isDone = lock.newCondition();
    }

    public static Order createOrder(){
        return new Order();
    }

    public void add(Food food){
        synchronized (food) {
            order.add(food);
        }
    }

    public LinkedList<Food> getOrder() {
        return order;
    }

    public int getOrderNum() {
        return orderNum;
    }

    public synchronized boolean isEmpty(){
        return this.order.isEmpty();
    }
}

Then assume that I have two threads called Customer and Cook. Cook will try to empty the LinkedList of the shared Order object, while Customer will constantly check whether his order has been fulfilled, e.g become empty. My purpose is to use a lock inside shared Order object. When Cook finish his job, he would call order.getIsDone().signal() to awake Customer thread awaiting with the same condition, e.g while(!order.isEmpty()) order.getIsDone().await();

The reason that I came up with this idea is that I want to share the same lock between two threads while bounding to a certain Order object. However, since Order object is shared between different threads, it might have concurrent issue. So, I am not sure this impelementation is a wise choice.

An alternative I can think of would be replacing order.getIsDone().await() with yield(), but it will cause some performance loss while it's much simplier.


Solution

  • The whole idea of locks it that they are shared between threads!

    So yes, do lock on the lock which is a field of your object, and use the condition to check if the order is completed.

    Just remember you need always unlock the lock after locking so the pattern is

    lock.lock();
    try {
    ...
    } finally {
      lock.unlock();
    }
    

    You wait on your condition in the loop (but you already know about it).

    If you are already using lock, you dont need synchronized on your other two methods (isEmpty, add) as lock is faster so use it instead.