I have a couple of questions regarding Unsafe.park
and Object.wait
(and their corresponding resume methods):
Unsafe.park
over Object.wait
?So a parked thread is a thread blocked using LockSupport.
TIMED_WAITING The thread is waiting for another thread to perform an action for up to a specified waiting time.
More specifically, LockSupport provides an alternative for some of Thread's deprecated methods: suspend() and resume(). It uses a concept of permit and parking to detect if given thread should block or not.
When you call a park method on a Thread, it disables the thread for thread scheduling purposes unless the permit is available. You can call unpark method to make available the permit for the given thread, if it was not already available. So, when your Thread is in WAITING mode by LockSupport.
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!
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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With