Посмотрев на байт-код, сгенерированный для аналогичного случая, мы видим, что адрес объекта поля блокировки дублируется и используется для получения и снятия блокировки.Таким образом, оригинальный объект блокировки используется для блокировки.
После того как вы измените поле блокировки новым объектом внутри синхронизированного блока, другой поток может получить блокировку для нового объекта и войти в блок синхронизированного кода.Использование синхронизации, как это, не обеспечивает синхронизацию между потоками.Вы должны использовать другой объект для блокировки.(например, конечный объект cacheLock = new Object ())
Только для информационных целей этот вид использования не предотвращает сборку мусора.Поскольку упомянутые здесь адреса объектов находятся внутри стекового фрейма этого метода, как только метод завершит выполнение, стековый фрейм будет уничтожен, и ссылка на старый объект не останется.(Но не используйте синхронизированный таким образом.)
Вы можете проверить набор инструкций JVM здесь
public class SyncTest {
private Long value;
public static void main(String[] args) {
new SyncTest().testLock();
}
public SyncTest() {
value = new Long(1);
}
private void testLock() {
synchronized (this.value) {
this.value = new Long(15);
}
}
}
private void testLock();
0 aload_0 [this]
1 getfield t1.SyncTest.value : java.lang.Long [27] // push value field to operand stack
4 dup // duplicate value field push it to operand stack
5 astore_1 // store the top of the operand stack at [1] of local variable array (which is the duplicated value field)
6 monitorenter // aquire lock on top of the operand stack
7 aload_0 [this]
8 new java.lang.Long [22]
11 dup
12 ldc2_w <Long 15> [31]
15 invokespecial java.lang.Long(long) [24]
18 putfield t1.SyncTest.value : java.lang.Long [27]
21 aload_1 // load the [1] of local variable array to the operand stack (which is the duplicated value field previously stored)
22 monitorexit // release the lock on top of the operand stack
23 goto 29
26 aload_1
27 monitorexit
.....