24

I have a couple of questions regarding Unsafe.park and Object.wait (and their corresponding resume methods):

  1. Which one should be used in general?
  2. Which one has better performance?
  3. Is there any advantage to using Unsafe.park over Object.wait?
Rann Lifshitz
  • 4,040
  • 4
  • 22
  • 42
BrainStorm.exe
  • 1,565
  • 3
  • 23
  • 40
  • 4
    Do not use Unsafe, it is a jvm specific class, and using it destroys the purpose of using java in the first place. – Jazzwave06 Oct 23 '14 at 18:09
  • Exactly! Unsafe is not a documented JDK class. If you want to compare with something, compare with the `locks` in java.util.concurrent.locks – Lolo Oct 23 '14 at 18:11

4 Answers4

26

Most efficient wait is LockSupport.park/unpark, which doesn't require nasty (direct) usage of Unsafe, and doesn't pay to resynchronize your thread's local cache of memory.

This point is important; the less work you do, the more efficient. By not synchronizing on anything, you don't pay to have your thread check with main memory for updates from other threads.

In most cases, this is NOT what you want. In most cases, you want your thread to see all updates that happened "before now", which is why you should use Object.wait() and .notify(), as you must synchronize memory state to use them.

LockSupport allows you to safely park a thread for a given time, and so long as no other thread tries to unpark you, it will wait for that long (barring spurious wake ups). If you need to wait for a specific amount of time, you need to recheck the deadline and loop back into park() until that time has actually elapsed.

You can use it to "sleep" efficiently, without another thread to have to wake you up via LockSupport.parkNanos or .parkUntil (for millis; both methods just call Unsafe for you).

If you do want other threads to wake you up, chances are high that you need memory synchronization, and should not use park (unless carefully orchestrating volatile fields without race conditions is your thing).

Good luck, and happy coding!

Ajax
  • 2,465
  • 23
  • 20
  • 5
    Also, beware that a thread can, technically "spuriously unpark", so if you need to use the timeouts to wait at least `n` nanos, you will want to monitor that state and re-enter a parked state until your condition elapses. – Ajax May 12 '16 at 09:21
16

You're not supposed to use either of these methods if you're an application programmer.

They are both too low level, easy to screw up and not meant to be used outside libraries.

Why not try to use a higher level construct like java.util.concurrent.locks ?

To answer your question. park(...) works directly on the thread. It takes the thread as a parameter and puts it to sleep until unpark is called on the thread, unless unpark has already been called.

It's supposed to be faster than Object.wait(), which operates on the monitor abstraction if you know which thread you need to block/unblock.

Btw unpark is not really that Unsafe if used from inside Java:

public native void unpark(Object thread)

Unblock the given thread blocked on park, or, if it is not blocked, cause the subsequent call to park not to block. Note: this operation is "unsafe" solely because the caller must somehow ensure that the thread has not been destroyed. Nothing special is usually required to ensure this when called from Java (in which there will ordinarily be a live reference to the thread) but this is not nearly-automatically so when calling from native code.

Rann Lifshitz
  • 4,040
  • 4
  • 22
  • 42
Maxaon3000
  • 1,109
  • 8
  • 15
  • 3
    yeah maybe not to be used, but you see them in thread dumps so is better to know what they mean – ejaenv Jan 17 '19 at 07:30
5

LockSupport.park/unpark has better performance, but it's too low level API.

Besides, they have some different operations maybe you should notice:

    Object lockObject = new Object();
    Runnable task1 = () -> {
        synchronized (lockObject) {
            System.out.println("thread 1 blocked");
            try {
                lockObject.wait();
                System.out.println("thread 1 resumed");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    };
    Thread thread1 = new Thread(task1);
    thread1.start();

    Runnable task2 = () -> {
        System.out.println("thread 2 running ");
        synchronized (lockObject) {
            System.out.println("thread 2 get lock");
            lockObject.notify();
        }
    };
    Thread thread2 = new Thread(task2);
    thread2.start();

In this case, thread2 can get lock and notify the thread1 to resumed, because lockObject.wait(); will release the lock.

    Object lockObject = new Object();
    Runnable task1 = () -> {
        synchronized (lockObject) {
            System.out.println("thread 1 blocked");
            LockSupport.park();
            System.out.println("thread 1 resumed");

        }
    };
    Thread thread1 = new Thread(task1);
    thread1.start();

    Runnable task2 = () -> {
        System.out.println("thread 2 running ");
        synchronized (lockObject) {
            System.out.println("thread 2 get lock");
            LockSupport.unpark(thread1);
        }
    };
    Thread thread2 = new Thread(task2);
    thread2.start();

However, if you use LockSupport.park/unpark like this, it will cause dead lock. because thread1 won't release the lock by using LockSupport.park. therefore, thread1 can't resumed.

So be careful, they have different behaviors besides blocking the thread. And in fact, there are some Class we can use it conveniently to coordinate in multi-thread environment, such as CountDownLatch, Semaphore, ReentrantLock

Wang Kenneth
  • 387
  • 3
  • 7
  • 1
    Synchronized keyword doesn't work together with LockSupport.park or unpark. It only works with Object.wait or notify. Thats why the thread in the 2nd example is blocked. – Raj kannan Iyyappan Feb 26 '20 at 20:03
  • true, but I guess the answer aims at the difference, and this actually is the difference between the two. – CHANist Aug 28 '22 at 23:48
3

If you're managing concurrency with synchronized blocks, then you would use Object.wait, notify, and notifyAll for signalling. This is the first kind of concurrency control that Java supported, and it was considered to be very easy to use at the time. It certainly was, compared to everything else that was around.

These days, though, there are lots of classes in java.util.concurrent don't require as much specialized knowledge to work with. These are the things that should be used by average programmers these days.

The park* and unpark methods in LockSupport are what you would use if you are writing your own lock-free algorithms and data structures. They are high-performance constructs that don't require locks to work with, and they are very well designed to make this kind of work as easy as it can be... but that is still very difficult and tricky work that is best left to experts.

Matt Timmermans
  • 53,709
  • 3
  • 46
  • 87