Java 为什么这个同步块似乎需要很长时间才能获得锁?
我不熟悉java中的多线程,我有一个问题,有些人可能会觉得很琐碎 我必须调试一段第三方代码,我需要一些基本信息,知道在哪里查找问题,因为代码非常大 当以下代码运行时:Java 为什么这个同步块似乎需要很长时间才能获得锁?,java,android,multithreading,synchronized,Java,Android,Multithreading,Synchronized,我不熟悉java中的多线程,我有一个问题,有些人可能会觉得很琐碎 我必须调试一段第三方代码,我需要一些基本信息,知道在哪里查找问题,因为代码非常大 当以下代码运行时: public void method() { long startTime = System.currentTimeMillis(); synchronized (obj) { log( "time:" + System.currentTimeMillis() - startTime + " ms"
public void method()
{
long startTime = System.currentTimeMillis();
synchronized (obj)
{
log( "time:" + System.currentTimeMillis() - startTime + " ms" );
...
}
}
我得到:
11:13:12 - time: 3816 ms
...
11:14:14 - time: 0 ms
为什么要花这么长时间(3816毫秒)才能获得对象的锁?
我应该去哪里看?例如,我可以想象一个可能的答案是寻找获得“obj”锁的代码,即块,例如:
synchronized (obj) { ... }
或者,在对象“obj”上进行任何没有“synchronized”的修改是否也可以锁定该对象?您需要检查以下内容:
- 在您的obj类中是否有任何方法/块,这些方法/块是在此基础上同步的。如果是,那么必须有多个线程,其中一个是您上面的代码,而另一个可能使用相同obj的方法
- 你都在哪里分享obj?如果它是由多个类共享的,那么检查是谁锁定了同一个对象
- 作为jdk一部分的实用程序可以帮助实现这一点。
-l
(长列表)选项将打印各个线程持有的所有锁。如果您可以在问题发生时捕获程序,那么您可以找到另一个持有锁的线程。您可以通过查找线程,查看它正在等待的条件对象,然后在堆栈的其余跟踪中搜索该条件对象来实现这一点
这有关于如何查看线程转储的更详细信息。如果线程需要那么长时间才能获得锁,那是因为当前有其他人持有它 你应该注意两件事:
obj
属于MyObject
类型,那么您应该寻找如下方法:
public class MyObject{
public synchronized void myMethod() {
...
}
}
因为它们本质上和
public class MyObject{
public void myMethod() {
synchronized (this) {
...
}
}
}
因此,如果一个线程正在执行obj.myMethod()
,那么想要进入synchronized(obj)
块的线程将不得不等待,因为它们都锁定在同一个对象上。顺便说一下,这就是为什么我强烈建议不要使用同步方法语法,总是锁定私有(或受保护)类成员的原因您可以使用或获取所有线程的当前执行状态及其所持有的锁的快照。如果您使用的是android,请参阅如何在那里获取线程转储的答案。这是您获得的第一个日志吗?@CommuSoft是这是我获得的第一个日志got@fge对象是一个复杂的对象。它被用在许多不同的班级,做许多不同的事情。你能解释一下你为什么问这个问题,这样我才能给你一个准确的答案吗?@CommuSoft如果这是Oracle的JVM,因此是热点,那么优化只有在10000次执行之后才会开始触发;因此,我怀疑JIT是否是你在锁着的浴室门外等待的答案。为什么在那里花了这么长时间?这个问题的答案绝对与门锁的质量无关,而与房间里的人有关。记住,使用锁的建议是:(1)不要花任何时间在锁上,以避免争用;(2)拥有尽可能少的锁,以避免死锁错误。聪明的读者会注意到,这两条极好的建议实际上是对立的,这会在尝试遵循时产生问题。有趣的游戏,并发;赢得比赛的唯一方法是不要玩。你还应该看看
obj
是否不仅与synchronized
一起使用,还与notify
和lock
一起使用,它们可以进行交互。应该避免混合使用这两种同步模式,但在调试第三方代码时,您应该做好最坏的准备。是的,但请注意,如果没有通知,OP的输出将永远不会打印任何内容。而且,很明显,如果您使用synchronize,那么您将使用notify/wait,但正如您所说的,是的,不能确定。@dritan是的,当然。我现在已经添加了这个细节。为了避免这个问题,我强烈建议您避免声明同步方法,而是在私有类成员上同步。您可以使用jvisualvm的Threads选项卡或Jstack拍摄所有线程的当前执行状态及其持有的锁的快照。==>我正在android上开发。你对这种环境有什么建议吗?@dritan我没有,但堆栈溢出有-只是注意到答案的第2点是相关的。其他线程调用了其他同步方法。问题标记为[android],因此JDK实用程序可能不是最有用的。@fadden发布此答案时,它没有标记为“android”
public class MyObject{
public void myMethod() {
synchronized (this) {
...
}
}
}