关于Unsafe.park
和Object.wait
(及其相应的简历方法),我有几个问题:
Unsafe.park
优于Object.wait
?答案 0 :(得分:14)
最有效的等待是LockSupport.park/unpark
,它不需要令人讨厌(直接)使用Unsafe,并且不需要为重新同步线程的本地内存缓存付费。
这一点很重要;你做的工作越少,效率越高。通过不同步任何事情,您不需要付费让线程检查主内存以获取其他线程的更新。
在大多数情况下,这不是你想要的。在大多数情况下,您希望您的线程看到发生的所有更新"之前",这就是您应该使用Object.wait()和.notify()的原因,因为您必须同步内存状态才能使用它们
LockSupport允许您在给定时间内安全地停放线程,并且只要没有其他线程试图取消您,它将等待那么长时间(禁止虚假唤醒)。如果您需要等待一段特定的时间,您需要重新检查截止日期并循环回park()直到该时间已经过去。
你可以用它来睡觉"有效率,没有其他线程必须通过LockSupport.parkNanos
或.parkUntil
唤醒您(对于millis;这两种方法都只是为您调用Unsafe)。
如果您确实希望其他线程唤醒您,那么您可能需要内存同步,并且不应该使用park(除非仔细编排没有竞争条件的易失性字段)。
祝你好运,编码愉快!
答案 1 :(得分:12)
如果您是应用程序员,那么您不应该使用这些方法中的任何一种。
它们的级别太低,容易搞砸,不适合在图书馆外使用。
为什么不尝试使用更高级别的构造,如java.util.concurrent.locks?
回答你的问题。 park(...)直接在线程上工作。它将线程作为参数并将其置于休眠状态,直到在线程上调用unpark,除非已经调用了unpark。
它应该比Object.wait()更快,如果你知道需要阻塞/解除阻塞的线程,它就会在监视器抽象上运行。
顺便说一句,unpark并不是那么不安全if used from inside Java:
public native void unpark(Object thread)
取消阻止给定的帖子 在公园被阻止,或者,如果没有被阻止,则导致后续呼叫 停车不要阻挡。注意:此操作不安全"完全是因为 调用者必须以某种方式确保线程没有被销毁。 调用时通常不需要特殊的东西来确保这一点 Java(通常会有一个实时引用 线程)但是这几乎不是自动的,所以在调用时 本地代码。
答案 2 :(得分:2)
LockSupport.park/unpark
具有更好的性能,但是它的API级别太低。
此外,它们有一些不同的操作,也许您应该注意:
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();
在这种情况下,线程2可以获取锁并通知线程1恢复,因为lockObject.wait();
将释放该锁。
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();
但是,如果您像这样使用LockSupport.park/unpark
,将导致死锁。因为thread1不会使用LockSupport.park
释放锁。因此,thread1无法恢复。
所以要小心,它们除了阻塞线程外还有其他行为。实际上,有些类可以方便地在多线程环境中使用它来进行协调,例如CountDownLatch, Semaphore, ReentrantLock
答案 3 :(得分:2)
如果您要管理synchronized
块的并发性,则可以使用Object.wait
,notify
和notifyAll
进行信令。这是Java支持的第一种并发控制,当时被认为非常易于使用。与周围的其他一切相比,肯定是这样。
但是,如今,java.util.concurrent
中有很多类不需要太多专业知识来使用。这些是当今普通程序员应该使用的东西。
如果编写自己的无锁算法和数据结构,则将使用park*
中的unpark
和LockSupport
方法。它们是高性能的结构,不需要使用锁,并且经过精心设计,可以使这种工作尽可能简单……但这仍然是非常困难和棘手的工作,最好留下来给专家。