Java 以下同步块的用途是什么?

Java 以下同步块的用途是什么?,java,synchronization,opencms,Java,Synchronization,Opencms,我在OpenCMS上遇到了稳定性问题。当我执行线程转储时,许多线程(400)正在等待以下代码中的synchronized(m_processingFiles)块: public class CmsJspLoader ... { ... private static Set m_processingFiles = Collections.synchronizedSet(new HashSet()); ... ... ... public String updateJsp(...) { ....

我在OpenCMS上遇到了稳定性问题。当我执行线程转储时,许多线程(400)正在等待以下代码中的
synchronized(m_processingFiles)
块:

public class CmsJspLoader ... {
...

private static Set m_processingFiles = Collections.synchronizedSet(new HashSet());

...
...
...
public String updateJsp(...) {
....

while (m_processingFiles.contains(jspVfsName)) {
    // wait a little bit until the first thread finishes
    try {
        synchronized (m_processingFiles) {
            m_processingFiles.wait(100);
        }
    } catch (InterruptedException e) {
        // ignore
    }
}
...
}
...
}
该代码是OpenCMS的一部分。 代码中没有
notify()
。显示的
sync
块内没有状态更改或读取共享变量。 然而,有400个线程在等待它,这意味着,只要通过这个
sync
最后一个线程应该等待40秒


我根本不明白它的目的。有什么我看不到的吗?

如果没有notify或notifyAll呼叫,这基本上就像100毫秒的睡眠。100毫秒后,线程将唤醒并继续。假设同步块中只有等待,那么这只是一种奇怪的睡眠方式。同步块触发事件可能会造成其他一些影响。因此,可能存在一些微妙的线程安全问题

在这里解释线程转储时需要注意的一点是,400个被阻塞的线程是在等待进入同步块还是在等待?当一个线程进入wait时,它实际上是从synchronized块释放锁,以便另一个线程可以进入它。当线程从等待中唤醒时,将重新获得锁

如果线程转储表示“等待监视器输入”,那么一个线程在同步块中,而所有其他线程都试图输入。这表明您在这里有一个主要的并发问题

但是,如果线程转储显示类似“in Object.wait()”的内容,则表示线程处于100ms等待状态,其他线程可以自由进入同步块。在本例中,这意味着循环条件仍然为false,因此请查看这一侧发生了什么,而不是等待线程发生了什么


也就是说,如果可以在进程的另一端执行notify/notifyAll,则可以减少唤醒和检查线程是否仍处于睡眠状态的延迟和成本。

如果没有notify或notifyAll调用,这基本上就像100毫秒的睡眠。100毫秒后,线程将唤醒并继续。假设同步块中只有等待,那么这只是一种奇怪的睡眠方式。同步块触发事件可能会造成其他一些影响。因此,可能存在一些微妙的线程安全问题

在这里解释线程转储时需要注意的一点是,400个被阻塞的线程是在等待进入同步块还是在等待?当一个线程进入wait时,它实际上是从synchronized块释放锁,以便另一个线程可以进入它。当线程从等待中唤醒时,将重新获得锁

如果线程转储表示“等待监视器输入”,那么一个线程在同步块中,而所有其他线程都试图输入。这表明您在这里有一个主要的并发问题

但是,如果线程转储显示类似“in Object.wait()”的内容,则表示线程处于100ms等待状态,其他线程可以自由进入同步块。在本例中,这意味着循环条件仍然为false,因此请查看这一侧发生了什么,而不是等待线程发生了什么

也就是说,如果可以在进程的另一端执行notify/notifyAll,那么无论进程的另一端是什么,它都将减少唤醒和检查线程是否仍处于睡眠状态的延迟和成本。

代码中必须有某个位置,线程将
jspVfsName
添加到
m\u processingFiles
,执行更多工作,然后从
m_processingFiles
中删除
jspVfsName
。如果不是这样,那么您的其他线程将永远在
while
循环中等待()。由于某种原因,实现者不希望任何其他线程在其他处理进行时执行
updateJsp

我建议您检查代码,看看
jspVfsName
实际上是什么,并找到可以从
m_processingFiles
中添加/删除它的代码位置。也许你也会明白为什么作者不想在
jspVfsName
位于
m\u processingFiles
时运行
updateJsp

一旦找到了,您可以检查“其他”代码,看看
jspVfsName
是否可以添加到
m_processingFiles
中,并且永远不会删除。如果是这样的话,这(自然)会导致livelock,这可以解释您的稳定性问题

或者可能是
updateJsp
被频繁调用,而修改
m\u processingFiles
的“其他”代码也被频繁调用,以至于导致了严重的并发瓶颈?您的应用程序是否有问题,导致调用
updateJsp
的次数超过了它应该调用的次数(可能是在每次请求时,而不是每次在服务器上放置新的JSP文件时)

如果
updateJsp
运行非常频繁,但这不是由应用程序的问题引起的,您可以尝试缩短
wait()
时间。这不会造成任何伤害——它只会让等待的线程更频繁地检查
jspVfsName
是否仍在
m_处理文件中。就CPU而言,100ms是一段很长的时间

代码中必须有某个位置,线程将
jspVfsName
添加到
m\u processingFiles
,执行更多的工作,然后从
m\u processingFiles
中删除
jspVfsName
。如果不是这样,那么您的其他线程将只是
wait()
foreve