简体   繁体   English

混淆使用Java中的synchronized:模式还是反模式?

[英]Confusing use of synchronized in Java: pattern or anti-pattern?

I'm doing a code review for a change in a Java product I don't own. 我正在对我不拥有的Java产品进行代码审查。 I'm not a Java expert, but I strongly suspect that this is pointless and indicates a fundamental misunderstanding of how synchronization works. 我不是Java专家,但我强烈怀疑这是毫无意义的,并且表明对同步如何工作的基本误解。

synchronized (this) {
    this.notify();
}

But I could be wrong, since Java is not my primary playground. 但我错了,因为Java不是我的主要操场。 Perhaps there is a reason this is done. 也许有这样做的原因。 If you can enlighten me as to what the developer was thinking, I would appreciate it. 如果你可以告诉我开发人员的想法,我将不胜感激。

It certainly is not pointless, you can have another thread that has a reference to the object containing the above code doing 它当然不是没有意义的,你可以有另一个线程,它包含对包含上述代码的对象的引用

synchronized(foo) {
    foo.wait();
}

in order to be woken up when something happens. 为了在事情发生时被唤醒。 Though, in many cases it's considered good practice to synchronize on an internal/private lock object instead of this . 但是,在许多情况下,在内部/私有锁定对象上进行同步而不是this操作被认为是一种良好的做法。

However, only doing a .notify() within the synchronization block could be quite wrong - you usually have some work to do and notify when it's done, which in normal cases also needs to be done atomically in regards to other threads. 但是, 在同步块中执行.notify()可能是非常错误的 - 您通常需要做一些工作并在完成时通知,这在正常情况下也需要以原子方式完成其他线程。 We'd have to see more code to determine whether it really is wrong. 我们必须看到更多代码来确定它是否真的错了。

If that is all that is in the synchonized block then it is an antipattern, the point of synchronizing is to do something within the block, setting some condition, then call notify or notifyAll to wake up one or more waiting threads. 如果那是同步块中的全部,则它反模式,同步点是在块内做某事,设置一些条件,然后调用notifynotifyAll来唤醒一个或多个等待线程。

When you use wait and notify you have to use a condition variable, see this Oracle tutorial : 当您使用wait并通知您必须使用条件变量时,请参阅此Oracle教程

Note: Always invoke wait inside a loop that tests for the condition being waited for. 注意:始终在测试等待条件的循环内调用wait。 Don't assume that the interrupt was for the particular condition you were waiting for, or that the condition is still true. 不要假设中断是针对您正在等待的特定条件,或者条件仍然是真的。

You shouldn't assume you received a notification just because a thread exited from a call to Object#wait, for multiple reasons: 你不应该假设你收到通知只是因为一个线程退出对Object#的调用等待,原因有多种:

  • When calling the version of wait that takes a timeout value there's no way to know whether wait ended due to receiving a notification or due to timing out. 当调用等待超时值的wait版本时,由于收到通知或由于超时而无法知道等待是否结束。

  • You have to allow for the possibility that a Thread can wake up from waiting without having received a notification (the "spurious wakeup"). 您必须允许线程可以在没有收到通知的情况下从等待中醒来(“虚假唤醒”)。

  • The waiting thread that receives a notification still has to reacquire the lock it gave up when it started waiting, there is no atomic linking of these two events; 接收通知的等待线程仍然必须重新获取它在开始等待时放弃的锁,这两个事件没有原子链接; in the interval between being notified and reacquiring the lock another thread can act and possibly change the state of the system so that the notification is now invalid. 在被通知和重新获取锁定之间的间隔中,另一个线程可以动作并可能改变系统的状态,使得通知现在无效。

  • You can have a case where the notifying thread acts before any thread is waiting so that the notification has no effect. 您可以遇到通知线程在任何线程等待之前起作用的情况,以便通知无效。 Assuming one thread will enter a wait before the other thread will notify is dangerous, if you're wrong the waiting thread will hang indefinitely. 假设一个线程将在另一个线程通知之前进入等待是危险的,如果你错了,等待线程将无限期挂起。

So a notification by itself is not good enough, you end up guessing about whether a notification happened when the wait/notify API doesn't give you enough information to know what's going on. 因此,通知本身不够好,您最终会猜测当wait / notify API没有提供足够的信息来知道发生了什么时,是否发生了通知。 Even if other work the notifying thread is doing doesn't require synchronization, updating the condition variable does; 即使通知线程正在进行的其他工作也不需要同步,更新条件变量的确如此; there should at least be an update of the shared condition variable in the synchronized block. 至少应该在synchronized块中更新共享条件变量。

This is perfectly fine. 这很好。 According to the Java 6 Object#notify() api documentation : 根据Java 6 Object#notify() api文档

This method should only be called by a thread that is the owner of this object's monitor. 此方法只应由作为此对象监视器所有者的线程调用。

This is generally not a anti-pattern, if you still want to use intrinsic locks. 如果您仍想使用内部锁,这通常不是反模式。 Some may regard this as an anti pattern, as the new explicit locks from java.util.concurrent are more fine grained. 有些人可能会将此视为反模式,因为来自java.util.concurrent的新显式锁更精细。

But your code is still valid. 但是你的代码仍然有效。 For instance, such code can be found in a blocking queue, when an blocking operation has succeeded and another waiting thread should be notified. 例如,当阻塞操作成功并且应该通知另一个等待线程时,可以在阻塞队列中找到这样的代码。 Note however that concurrency issues are highly dependent on the usage and the surrounding code, so your simple snippet is not that meaningful. 但请注意,并发问题在很大程度上取决于使用情况和周围的代码,因此您的简单代码段并没有那么有意义。

The Java API documentation for Object.notify() states that the method "should only be called by a thread that is the owner of this object's monitor". Object.notify()的Java API文档声明该方法“只应由作为此对象监视器所有者的线程调用”。 So the use could be legitimate depending upon the surrounding context. 因此,根据周围的环境,使用可能是合法的。

Here is a link to an earlier post. 这是以前帖子的链接。 To give you a clear answer we would need a bit more code of the class to calls the notify() . 为了给你一个明确的答案,我们需要更多的类代码来调用notify()

wait(), notify() and notifyAll() inside synchronized statement 在synchronized语句中的wait(),notify()和notifyAll()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM