- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
public class PLock {
private Map<Thread, Integer> readLocks = new HashMap<Thread, Integer>();
private Thread writeLock = null;
private int writeLockCount = 0;
public synchronized void getReadLock() {
Thread currentThread = Thread.currentThread();
long startTimeMillis = System.currentTimeMillis();
boolean gotStuck = false;
while (canClaimReadLock(currentThread) == false) {
gotStuck = true;
try {
wait();
} catch (InterruptedException ex) {
Log.warn("Interrupted while attempting to get read lock.", ex);
}
}
report(gotStuck, startTimeMillis, "read");
if (readLocks.containsKey(currentThread)) {
readLocks.put(currentThread, 1 + readLocks.get(currentThread));
} else {
readLocks.put(currentThread, 1);
}
}
...
public synchronized void relinquishReadLock() {
Thread currentThread = Thread.currentThread();
if (readLocks.containsKey(currentThread) == false) {
throw new RuntimeException("Cannot relinquish read lock on thread " + currentThread + " because it does not hold a lock.");
}
int newLockCount = readLocks.get(currentThread) - 1;
if (newLockCount == 0) {
readLocks.remove(currentThread);
notifyAll(); // IMPORTANT: allow other threads to wake up and check if they can get locks now.
} else {
readLocks.put(currentThread, newLockCount);
}
}
public synchronized void getWriteLock() {
//Log.warn("getWriteLock() in thread " + Thread.currentThread());
//dumpLocks();
Thread currentThread = Thread.currentThread();
long startTimeMillis = System.currentTimeMillis();
boolean gotStuck = false;
while (canClaimWriteLock(currentThread) == false) {
gotStuck = true;
try {
wait();
} catch (InterruptedException ex) {
Log.warn("Interrupted while attempting to get write lock.", ex);
}
}
report(gotStuck, startTimeMillis, "write");
writeLock = currentThread;
writeLockCount++;
}
...
public synchronized void relinquishWriteLock() {
//Log.warn("relinquishWriteLock() in thread " + Thread.currentThread());
Thread currentThread = Thread.currentThread();
if (writeLock != currentThread) {
throw new RuntimeException("Cannot relinquish write lock on thread " + currentThread + " because it does not hold the lock.");
}
if (writeLockCount <= 0) {
throw new RuntimeException("Tried to relinquish write lock on thread " + currentThread + " while write lock count is " + writeLockCount);
}
writeLockCount--;
if (writeLockCount == 0) {
writeLock = null;
notifyAll(); // IMPORTANT: allow other threads to wake up and check if they can get locks now.
}
}
...
}
bormand 30.05.2014 18:18 # +1
Супер. Нефиг треду знать, что его хотели тормознуть.
guest 30.05.2014 18:46 # +1
У нас на первом курсе препод за такое замечания делал.
3.14159265 31.05.2014 01:35 # 0
Более того даже простительно если код нормальный(м.б. кодстайл такой. не все ! замечают, NOTа на них нет)
Это мелочь. Вот за деревьями леса не видишь.
guest 31.05.2014 17:31 # 0
3.14159265 31.05.2014 18:20 # +1
На самом деле сомневаюсь что это писал первокурсник.
> on Aug 24, 2007 Initial Import.
Если это какой-то древний код написанный еще до 1.5 (на что намекает многое, тот же System.currentTimeMillis), то вполне сойдет. Небрежно конечно написано, но так вроде всё на месте.
kostoprav 31.05.2014 18:21 # 0
3.14159265 31.05.2014 18:25 # 0
Косвенные признаки, которые я сразу счёл говном - не имплементим интерфейс Lock, не используем nanoTime, атомики и волатилы идут лесом - только synchronized, только хардкор.
А вот если предположить что писалось для 1.4 и раньше - то всё стаёт на свои места и наверное единственный весомый факап, как отметил борманд
> catch (InterruptedException ex)
Еще и логгер стандартный - уже давно не пишут так.
>>Map<Thread, Integer>
Могли допилить позже, когда исправляли ворнинги.
3.14159265 31.05.2014 01:36 # +1
Пиздец. Сэкономил называется. Какая-то совершенно безумная premature optimization.
Типа много читателей один писатель, и в то же время всё работает в страшно однопоточном режиме.
bormand 31.05.2014 08:13 # 0
Ну все же не совсем в однопоточном. Монитор захватывается только на время взятия и отпускания R/W лока, в остальное время он отпущен. Поэтому если R/W лок берут надолго - может быть и потянет.
bormand 31.05.2014 08:37 # 0
3.14159265 31.05.2014 12:05 # 0
Нет. Но это не фатально. Так же устроены и Collections.synchronized...(). Работают как-то ведь.
>и хуй он дождется своей очереди,
Встроенную лочку не дураки писали, там очередь стоит. Правда не она всегда совсем уж честная.
Но тут вообще вероятность не на стороне writera.
bormand 31.05.2014 12:10 # +1
> вероятность
Не поможет же, хоть честная, хоть не честная. И вероятность его не спасет. Если reader'ов много, и нет таких моментов, когда все они отпустили лок (а это вполне нормальная ситуация для R/W lock'а, ради нее он и запилен) - canClaimWriteLock() никогда не вернет true, и writer соснет хуйца.
Нормальный R/W лок, емнип, обламывает reader'ов и ставит их в очередь если в ней есть writer. Тем самым writer получает преимущество.
3.14159265 31.05.2014 12:11 # 0
А с другой там может быть любое безумие. canClaimReadLock может возвращать false, если есть writer который дохера ждёт. Там вот startTimeMillis есть.
relinquishReadLock
bormand 31.05.2014 12:16 # 0
Дык она локальная.
Еще предположение: canClaimWriteLock может взводить некий флаг, который заставляет canClaimReadLock возвращать false. Вот только кто его сбросит?
3.14159265 31.05.2014 12:22 # +1
Ну тогда постоянные writerы будут обламывать readerы, что так же плохо.
>Вот только кто его сбросит?
При освобождении writeLock возле notify.
На самом деле там много разных стратегий есть. Смотреть по времени ожидания - это и есть честная.
Если например суммарное время ожидания читателей больше чем писателя, то дать им. Если наоборот - писать.
А там еще есть апгрейд лочки, если например есть ридер, который решил что-то писать. В общем целая наука.
bormand 31.05.2014 12:27 # +1
Ну все-таки, имхо, R/W лок выручает как раз когда writer работает достаточно редко, а reader'ов дохуя и больше, и захватывают лок они на достаточно большое время... Так что потерпят.
> На самом деле там много разных стратегий есть.
+1
3.14159265 31.05.2014 12:30 # 0
но даже ReentrantReadWriteLock старадает от этого
When constructed as non-fair (the default), the order of entry to the read and write lock is unspecified, subject to reentrancy constraints. A nonfair lock that is continuously contended may indefinitely postpone one or more reader or writer threads, but will normally have higher throughput than a fair lock.
bormand 31.05.2014 12:35 # 0
3.14159265 31.05.2014 12:46 # +1
А существует и даунгрейд, типа освободить W и взять R не дожидаясь готовности.
3.14159265 31.05.2014 01:38 # 0
На самом деле он не всегда хорош, часто обычная лочка его уделывает.
Вот StampedLock - отдельный разоговор.