Michael_user3015308
Michael_user3015308

Reputation: 45

Lock is lost when putting ReentrantLock into HashMap

I am trying to make several consumer threads that listen to one producer thread wait until the producer has something to publish. The code that I thought would work "loses" the lock on being put into and taken out of a shared class.

In a controller class I start the thread running with Server server = new Server();

    Thread serverThread = new Thread(server,"Server");
    serverThread.start(); 

    Consumer consumer = new Consumer();
    Thread consumerThread;
    for (int i =0;i<6;i++){
        consumerThread = new Thread(consumer,"Consumer No:"+i);
    consumerThread.start();
    server.threadRefs[i]= consumerThread;
    }

The consumer classes put details of threads into the Map as follows:

public class Consumer implements Runnable {

private ReentrantLock lock = new ReentrantLock();
private Condition cond = lock.newCondition();

@Override
public void run() {

    long threadID = Thread.currentThread().getId();
    while (true) {
        try {

            lock.lock();

            MDRequest.threadLocks.put(threadID, lock);
            System.out.println("Thread " + threadID + "  lock = " + lock.toString());

            cond.await();
            System.out.println("Thread " + threadID + "  done waiting");
        } catch (InterruptedException ex) {
            System.out.println("Interruped " + threadID);
        } finally {
            lock.unlock();
        }

        System.out.println("Finished " + threadID);
    }

}

The shared class is simply:

public class MDRequest {

    protected static ConcurrentHashMap<Long, ReentrantLock> threadLocks = new ConcurrentHashMap<Long, ReentrantLock>();

The Server has the following run() method:

public void run() {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException ex) {
        Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
    }
    Set<Long> keys = MDRequest.threadLocks.keySet();
    Long[] threadIDs = keys.toArray(new Long[1]);

    // generates a random series of numbers for each thread and notifies threads about them 
    while (true) {


        Random random = new Random();
        int threadRef = random.nextInt(5);

        System.out.println("About to signal thread " + threadIDs[threadRef].toString());

        // notify the thread of the data 
        ReentrantLock lock = MDRequest.threadLocks.get(threadIDs[threadRef]);
        System.out.println("Thread " + threadIDs[threadRef].toString() + "  lock = " + lock.toString());

        Condition cond = lock.newCondition();
        cond.signal();
        lock.unlock();

    }

The output is as follows:

Thread 11  lock = java.util.concurrent.locks.ReentrantLock@272d7a10[Locked by thread Consumer No:0]
Thread 12  lock = java.util.concurrent.locks.ReentrantLock@272d7a10[Locked by thread Consumer No:1]
Thread 13  lock = java.util.concurrent.locks.ReentrantLock@272d7a10[Locked by thread Consumer No:2]
Thread 14  lock = java.util.concurrent.locks.ReentrantLock@272d7a10[Locked by thread Consumer No:3]
Thread 15  lock = java.util.concurrent.locks.ReentrantLock@272d7a10[Locked by thread Consumer No:4]
Thread 16  lock = java.util.concurrent.locks.ReentrantLock@272d7a10[Locked by thread Consumer No:5]
About to signal thread 14
Thread 14  lock = java.util.concurrent.locks.ReentrantLock@272d7a10[Unlocked]
Exception in thread "Price Server" java.lang.IllegalMonitorStateException
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signal(AbstractQueuedSynchronizer.java:1885)
    at faster.Server.run(Server.java:46)
    at java.lang.Thread.run(Thread.java:695)

From the output int the Server class I can see that when I read the lock out of the Map it now has a status of "Unlocked". When it was put in it had a status of Locked on thread 14.

Why does the reference to the ReentrantLock "lose" the lock?

Is there a way of sharing the lock between the multiple consumer threads and the server thread in a way that the lock is not lost?

Upvotes: 2

Views: 1214

Answers (2)

JosefN
JosefN

Reputation: 952

The problem is thread in Server class tries to unlock but has not lock the Lock

lock.unlock();

Please see ReentrantLock documentation where is clearly stated:

If the current thread is not the holder of this lock then IllegalMonitorStateException is thrown.

Upvotes: 0

shazin
shazin

Reputation: 21923

The problem you are facing is may be because of the following line in Server

Condition cond = lock.newCondition();
cond.signal();
lock.unlock();

Do you need to call unlock() from the Server for a Lock which is locked by the Consumer? I think calling signal() will suffice.

Think about this.

Upvotes: 1

Related Questions