给定以下示例:
这个例子来自“Java Concurrency in Practice”。
有一条评论:
因为 Widget 和 LoggingWidget 中的 doSomething 方法都是同步的,所以在继续之前,它们都会尝试获取 Widget 上的锁。
为什么doSomething
LoggingWidget类中的方法需要获取锁Widget
?如果是这样,in 方法的onsynchronized
就没用了,对吧?doSomething
LoggingWidget
给定以下示例:
这个例子来自“Java Concurrency in Practice”。
有一条评论:
因为 Widget 和 LoggingWidget 中的 doSomething 方法都是同步的,所以在继续之前,它们都会尝试获取 Widget 上的锁。
为什么doSomething
LoggingWidget类中的方法需要获取锁Widget
?如果是这样,in 方法的onsynchronized
就没用了,对吧?doSomething
LoggingWidget
为什么LoggingWidget类中的doSomething方法需要获取Widget的锁?
假设您要确保 2 个线程不能LoggingWidget#doSomething()
同时运行(在此示例中,您可能要确保在 print 语句和对 的调用之间没有修改对象super.doSomething()
),那么您需要使方法同步.
如果你不这样做,一个线程(T1)可以开始运行LoggingWidget#doSomething()
并打印对象,另一个线程(T2)也可以开始运行LoggingWidget#doSomething()
(方法不同步)并再次打印对象,然后 T2 可以运行super.doSomething()
哪个(假设) 更改对象,然后 T1 将执行super.doSomething()
,但对象的状态将不同于它打印的状态,因为 T2 在此期间进行了更改。
防止这种情况的一种方法是使方法同步。
因为 Widget 和 LoggingWidget 中的 doSomething 方法都是同步的,所以在继续之前,它们都会尝试获取 Widget 上的锁。
可重入意味着给定线程可以重新获取它已经持有的锁。在这种情况下,当一个线程进入时,因为方法是同步的LoggingWidget#doSomething()
,所以它获取。this
当它运行时super.doSomething()
,它需要重新获取相同的锁。它的内在锁不是可重入的,线程将永远阻塞在那里,试图获取它已经持有并且不会释放 = 死锁的锁。
重入锁通过对已经被锁的对象重新获取锁来避免重复。现在,重要的是释放锁不会释放所有的锁。JVM内部维护计数器,每次在对象上获取锁时递增,每次释放锁时递减。因此,这种机制不仅避免了死锁,而且通过保持计数来保持正确性。