目前我正在学习Java并发编程。我注意到 Java 1.6 中引入的
LockSupport.park()
比 Object.wait()
更容易使用,Object.wait()
的典型用法就像
// Thread1
synchronized (lock) {
while (condition != true) {
lock.wait()
}
// do stuff
}
// Thread2
synchronized (lock) {
condition = true;
lock.notify();
}
我想我可以用
LockSupport.park()
重写它
// Thread1
while (condition != true) {
LockSupport.park();
}
// do stuff
// Thread2
condition = true;
LockSupport.unpark(Thread1);
通过使用
LockSupport.park()
,繁琐的synchroinzed
块消失。
我的问题是,我应该总是更喜欢
LockSupport.park()
而不是Object.wait()
吗? Object.wait()
有没有比LockSupport.park()
做的更好的地方比如性能?
wait 和 park 这两个东西,看起来很像,其实差别很大
调用
wait
发生在用synchronized
锁定的上下文中。您有线程正在测试的某些条件,您正在测试正在同步的对象的状态,以确定线程是否可以继续。等待/通知进程使用锁作为发送通知的中间人,操作系统调度程序负责通知谁。所以这里的用例是你有一些组件的状态你想保护它免受并发访问,如果一个线程不能取得进展,你希望它等到它可以。
其中的一个核心部分是您不是在等待线程中扮演木偶大师,您的代码不关心接下来通知谁,而是将其委托给调度程序。
对于公园来说,事情是不同的,没有像等待那样的锁定,它更像是一个动手的木偶大师。您的代码告诉各个线程休眠或唤醒。
LockSupport的API文档中有一个警告:
这些方法旨在用作创建更高级别同步实用程序的工具,它们本身对大多数并发控制应用程序没有用处。 park 方法仅设计用于以下形式的构造:
while (!canProceed()) { ... LockSupport.park(this); }
canProceed 或任何其他在调用 park 之前的操作都需要锁定或阻塞。因为每个线程只关联一个许可证,所以 park 的任何中间使用都可能干扰其预期效果。
您的具体示例取决于对条件测试的锁定,这是文档告诉您不要使用 park 的内容。等待之后,您还需要锁定“做事”部分。
如果你需要比
synchronized
更强大的东西,那么你可能会看看 ReentrantLock 或不同的非阻塞方法,但是 park/unpark 更像是一个低级的助手,而不是准备在你的应用程序中使用的东西。
请注意,在您的第二个示例中,您的条件需要是易变的或原子的,或者它的更新跨线程可见的其他内容。
不,Locksupport.park()/unpark() 不能替代 Object.wait()。
Locksupport.park()/unpark() 不需要你获取锁,但是 Object.wait() 确实需要你有 synchronized 关键字来保护它。让我告诉你为什么,
while (condition != true)
LockSupport.park();
在没有同步或其他保护的情况下,某些其他线程可以更改这两行代码之间的条件变量。因此,您的代码可能会在不应该停放/取消停放时停放/取消停放。