How do determine if an object is locked (synchronized) so not to block in Java?
Asked Answered
F

9

88

I have a process A that contains a table in memory with a set of records (recordA, recordB, etc...)

Now, this process can launch many threads that affect the records, and sometimes we can have 2 threads trying to access the same record - this situation must be denied. Specifically if a record is LOCKED by one thread I want the other thread to abort (I do not want to BLOCK or WAIT).

Currently I do something like this:

synchronized(record)
{
performOperation(record);
}

But this is causing me problems ... because while Process1 is performing the operation, if Process2 comes in it blocks/waits on the synchronized statement and when Process1 is finished it performs the operation. Instead I want something like this:

if (record is locked)
   return;

synchronized(record)
{
performOperation(record);
}

Any clues on how this can be accomplished? Any help would be much appreciated. Thanks,

Favouritism answered 22/11, 2009 at 20:0 Comment(0)
K
104

One thing to note is that the instant you receive such information, it's stale. In other words, you could be told that no-one has the lock, but then when you try to acquire it, you block because another thread took out the lock between the check and you trying to acquire it.

Brian is right to point at Lock, but I think what you really want is its tryLock method:

Lock lock = new ReentrantLock();
......
if (lock.tryLock())
{
    // Got the lock
    try
    {
        // Process record
    }
    finally
    {
        // Make sure to unlock so that we don't cause a deadlock
        lock.unlock();
    }
}
else
{
    // Someone else had the lock, abort
}

You can also call tryLock with an amount of time to wait - so you could try to acquire it for a tenth of a second, then abort if you can't get it (for example).

(I think it's a pity that the Java API doesn't - as far as I'm aware - provide the same functionality for the "built-in" locking, as the Monitor class does in .NET. Then again, there are plenty of other things I dislike in both platforms when it comes to threading - every object potentially having a monitor, for example!)

Kythera answered 22/11, 2009 at 20:7 Comment(7)
Yes. That's a good point. I took the example code literally, whereas the above is definitely a more robust implementationManchineel
But how do I use a lock per record? Currently the records are stored in a HashTable of records ... so I need a matching Hashtable of Locks? I am trying to ensure I have the most possible concurrency, so if a process wants to access recordC that should be fine (if only recordB is locked) - I use a global LOCK then it is essentially the same as locking the entire hashtable. ... that make any sense?Favouritism
@Shaitan00: The easiest way would be to have a lock within the record. Basically you want one lock associated with each record - so put it in the object.Kythera
Obviously :) I assume that I do not need to manage unlocking? Meaning when it exits the {} of the .tryLock() it will automatically & immediatly be unlocked correct?Favouritism
You do need to manage unlocking. See the tutorial I referenced in my answer and the unlock() method in the finally{} blockManchineel
From the sample provided by Jon there is no try/catch/finally - and from reading the tryLock() the lock is aquired automatically if it returns true. So ... how do I manage the unlocking? if (lock.tryLock() { do the work lock.unlock(); } else { throw exception locked }Favouritism
@Shaitan00: If you follow the link there's more example code. You put the try/finally inside the if (lock.tryLock()) basically.Kythera
M
26

Take a look at the Lock objects introduced in the Java 5 concurrency packages.

e.g.

Lock lock = new ReentrantLock()
if (lock.tryLock()) {
   try {
      // do stuff using the lock...
   }
   finally {
      lock.unlock();
   }
}
   ...

The ReentrantLock object is essentially doing the same thing as the traditional synchronized mechanism, but with more functionality.

EDIT: As Jon has noted, the isLocked() method tells you at that instant, and thereafter that information is out of date. The tryLock() method will give more reliable operation (note you can use this with a timeout as well)

EDIT #2: Example now includes tryLock()/unlock() for clarity.

Manchineel answered 22/11, 2009 at 20:2 Comment(0)
A
17

I found this, we can use Thread.holdsLock(Object obj) to check if an object is locked:

Returns true if and only if the current thread holds the monitor lock on the specified object.

Note that Thread.holdsLock() returns false if the lock is held by something and the calling thread isn't the thread that holds the lock.

Adolfoadolph answered 20/1, 2015 at 11:13 Comment(0)
G
14

Whilst the above approach using a Lock object is the best way to do it, if you have to be able to check for locking using a monitor, it can be done. However, it does come with a health warning as the technique isn't portable to non Oracle Java VMs and it may break in future VM versions as it isn't a supported public API.

Here is how to do it:

private static sun.misc.Unsafe getUnsafe() {
    try {
        Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        return (Unsafe) field.get(null);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

public void doSomething() {
  Object record = new Object();
  sun.misc.Unsafe unsafe = getUnsafe(); 
  if (unsafe.tryMonitorEnter(record)) {
    try {
      // record is locked - perform operations on it
    } finally {
      unsafe.monitorExit(record);
    }
  } else {
      // could not lock record
  }
}

My advice would be to use this approach only if you cannot refactor your code to use java.util.concurrent Lock objects for this and if you are running on an Oracle VM.

Gifferd answered 21/3, 2012 at 16:42 Comment(4)
@alestanis, I don't agree. Here I am reading these answers today and happy for all answer/comments, no matter when they are given.Musty
@Musty These answers are flagged by the SO system, that's why I left a message. You'll see when you start reviewing :)Uttica
@alestanis, I think that is unfortunate - I often see old questions that have an accepted answers, but that also have newer, better answers either because tech changes or because the accepted answer wasn't actually completely correct.Musty
@Tobias it's wrong. Thread.holdsLock(object) return true means the current thread hold the lock. However, if it return false, perhaps no thread hold the lock.Priest
A
4

While the Lock answers are very good, I thought I'd post an alternative using a different data structure. Essentially, your various threads want to know which records are locked and which aren't. One way to do this is to keep track of the locked records and make sure that data structure has the right atomic operations for adding records to the locked set.

I will use CopyOnWriteArrayList as an example because it's less "magic" for illustration. CopyOnWriteArraySet is a more appropriate structure. If you have lots and lots of records locked at the same time on average then there may be performance implications with these implementations. A properly synchronized HashSet would work too and locks are brief.

Basically, usage code would look like this:

CopyOnWriteArrayList<Record> lockedRecords = ....
...
if (!lockedRecords.addIfAbsent(record))
    return; // didn't get the lock, record is already locked

try {
    // Do the record stuff
}        
finally {
    lockedRecords.remove(record);
}

It keeps you from having to manage a lock per record and provides a single place should clearing all locks be necessary for some reason. On the other hand, if you ever have more than a handful of records then a real HashSet with synchronization may do better since the add/remove look-ups will be O(1) instead of linear.

Just a different way of looking at things. Just depends on what your actual threading requirements are. Personally, I would use a Collections.synchronizedSet( new HashSet() ) because it will be really fast... the only implication is that threads may yield when they otherwise wouldn't have.

Anglo answered 22/11, 2009 at 23:27 Comment(1)
If you want to have full control, you could put this in your own Lock class I guess.Tripinnate
I
2

Another workaround is (in case of you didnt have chance with the answers given here )is using timeouts. i.e. below one will return null after 1 second hanging:

ExecutorService executor = Executors.newSingleThreadExecutor();
        //create a callable for the thread
        Future<String> futureTask = executor.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return myObject.getSomething();
            }
        });

        try {
            return futureTask.get(1000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            //object is already locked check exception type
            return null;
        }
Instalment answered 3/6, 2016 at 10:10 Comment(0)
I
0

I needed to also find a solution to this, so searched the Java Concurrency API and came across StampedLock. The project is using Java 8. I am working in a heavily-threaded asynchronous data service that communicates with a native library and contains long-living configuration objects, necessitating sometimes-complex concurrency logic; thankfully this turned out to be relatively simple with the StampedLock class.

StampedLock has a method called tryOptimisticRead which does not wait, it just returns the status in the form of a long-time time stamp, where zero (0) indicates an exclusive lock is held. I then do delay for up to a second but you could just use the function without any sort of delay.

Here's how I'm detecting whether or not there's an exclusive lock, this paradigm is used in multiple locations and includes error handling:

    int delayCount = 0;

    //Makes sure that if there is data being written to this field at
    // this moment, wait until the operation is finished writing the
    // updated data.
    while (data1StampedLock.tryOptimisticRead() == 0)
    {
        try
        {
            delay(WRITE_LOCK_SHORT_DELAY);
            delayCount += 1;
        }
        catch (InterruptedException e)
        {
            logError("Interrupted while waiting for the write lock to be
                       released!", e);
            Thread.currentThread().interrupt();

            //There may be an issue with the JVM if this occurs, treat
            // it like we might crash and try to release the write lock.
            data1StampedLock.tryUnlockWrite();
            break;
        }

        if (delayCount * WRITE_LOCK_SHORT_DELAY > TimeUnit.SECONDS.toMillis(1))
        {
            logWarningWithAlert("Something is holding a write lock on" +
                " the data for a very, very long time (>1s). This may" +
                " indicate a problem that could cause cascading" +
                " problems in the near future." +
                " Also, the value for the data that is about to be" +
                " retrieved could potentially be invalid.");
            break;
        }
    }

    long nonExclusiveLockStamp = data1StampedLock.readLock();
    Data data1NonVolatile = data1;
    data1StampedLock.unlockRead(nonExclusiveLockStamp);
    
    return data1NonVolatile;

The read locks on a StampedLock are non-exclusive and are like reading from a thread-safe Map or HashTable, where it is multi-read/single-write.

Here is how I am using the exclusive lock to communicate to other threads that the instance data is being written to:

    long d1LockStamp = data1StampedLock.writeLock();
    this.data1 = data1;
    data1StampedLock.unlockWrite(d1LockStamp);

So if you wanted to only check whether or not something is locked at any given moment, you need only something simple like the following statement to get the status:

    boolean data1IsLocked = data1StampedLock.tryOptimisticRead() == 0;

Then check the value of that boolean.

There are, of course, the caveats and Here Be Dragons information mentioned in other answers (namely that the information is immediately stale), but if you really need to lock something and check that lock from another thread, this seemed to me to be the most reasonable, safe, and effective way that uses the java.util.concurrency package with no external dependencies.

Implicit answered 7/4, 2021 at 23:15 Comment(0)
C
0

If you just want to find out whether it is locked or not, you can use the code below.

public boolean isLocked() {
  try {
    return lock.tryLock();
  } finally {
    lock.unlock();
  }
}
Carroll answered 24/11, 2023 at 13:57 Comment(0)
F
-2

Thanks for this, it helped me out solving a race condition. I changed it a little to wear both belt and suspenders.

So here is my suggestion for AN IMPROVEMENT of the accepted answer:

You can ensure that you get safe access to the tryLock() method by doing something like this:

  Lock localLock = new ReentrantLock();

  private void threadSafeCall() {
    boolean isUnlocked = false;

    synchronized(localLock) {
      isUnlocked = localLock.tryLock();
    }

    if (isUnlocked) {
      try {
        rawCall();
      }
      finally {
        localLock.unlock();
      }
    } else {
      LOGGER.log(Level.INFO, "THANKS! - SAVED FROM DOUBLE CALL!");
    }
  }

This would avoid the situation where you might get two calling tryLock() at the almost same time, causing the return to be potentially doubt full. I'd like to now if I'm wrong, I might be over cautios here. But hey! My gig is stable now :-)..

Read more on my development issues at my Blog.

Flatten answered 15/9, 2016 at 10:17 Comment(2)
using synchronized on a lock object is very, very bad. The whole point of a lock is to avoid synchronized, so you can timeout or return quickly when you can't get the lock.Ascendancy
I don't even understand what you are trying to improve here. You are not adding anything over simply using tryLock.Sputnik

© 2022 - 2025 — McMap. All rights reserved.