Чтобы понять, почему exclusiveOwnerThread
не нужно быть волатильным, полезно взглянуть на методы получения и выпуска вместе.
Метод получения 1 :
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
@ReservedStackAccess
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
Метод выпуска:
@ReservedStackAccess
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
Также важно понимать, что exclusiveOwnerThread
не ссылается на какой-либо произвольный объект, не имеющий отношения к вовлеченным потокам. В частности, он содержит ссылку на экземпляр Thread
и строго сравнивается с вызывающим потоком. Другими словами, имеет значение, если:
Thread.currentThread() == getExclusiveOwnerThread()
Что будет истинно, если и только если вызывающий поток ранее вызвал #setExclusiveOwnerThread(Thread)
, с самим собой в качестве аргумента, из-за объединенной природы #nonfairTryAcquire(int)
и #tryRelease(int)
. Действия в одном потоке всегда происходят до последующие действия в том же потоке.
Так что если c != 0
, то есть два сценария ios:
Вызывающему потоку принадлежит синхронизатор.
- Поскольку действия в одном потоке всегда произойдет-до последующие действия в том же потоке гарантированно
getExclusiveOwnerThread()
вернут ссылку на вызывающий поток.
вызывающий поток не не владеет синхронизатором.
Больше не имеет значения, какая ссылка возвращается getExclusiveOwnerThread()
, поскольку для этого невозможно метод для возврата ссылки на вызывающий поток.
Вызывающий поток никогда не сможет увидеть устаревшую ссылку на себя из-за вызова setExclusiveOwnerThread(null)
в #tryRelease(int)
. Это означает, что getExclusiveOwnerThread()
может возвращать null
или какую-либо другую ссылку Thread
(устаревшую или нет), но не ссылку на вызывающий поток.
Причина, по которой state
должен быть изменчивым, заключается в том, что он распределяется между потоками таким образом, что первостепенно каждый поток видит самое последнее значение.
1. Реализация FairSync#tryAcquire(int)
имеет почти такую же реализацию, за исключением того, что учитывает порядок вызова потоков.