Не знаю, можно ли другим примитивом, но если делать так, как написано, то тебе надо использовать computeIfAbsent, который для ConcurrentHashMap — атомарный, т.о. избавишься от громоздкости.
Решение такой проблемы есть в книжке Java Concurrency In Practice, называется Memorizer. С виду оне конечно замороченное, на работать будет и на java 1.5
public class Memorizer<A, V> implements Computable<A, V> {
private final ConcurrentMap<A, Future<V>> cache
= new ConcurrentHashMap<A, Future<V>>();
private final Computable<A, V> c;
public Memorizer(Computable<A, V> c) { this.c = c; }
public V compute(final A arg) throws InterruptedException {
while (true) {
Future<V> f = cache.get(arg);
if (f == null) {
Callable<V> eval = new Callable<V>() {
public V call() throws InterruptedException {
return c.compute(arg);
}
};
FutureTask<V> ft = new FutureTask<V>(eval);
f = cache.putIfAbsent(arg, ft);
if (f == null) { f = ft; ft.run(); }
}
try {
return f.get();
} catch (CancellationException e) {
cache.remove(arg, f);
} catch (ExecutionException e) {
throw launderThrowable(e.getCause());
}
}
}
}
Re[2]: Чем заменить synchronized (string.intern())
vsb>но многие пишут, что так не стоит делать, это засорение intern-пула,
Начиная с Java7 этот пул в обычном хипе и, соответственно, там даже мусор собирается.
vsb>теоретический дедлок, если в другом месте случайно по той же строке будет синхронизация и тд.
Вот это верно — никогда нельзя делать синхронизацию на объектах, жизненный цикл которых ты не контролируешь от и до.
vsb>но как-то это всё сложно выглядит. Не упускаю ли я какой-то готовый примитив синхронизации?
Не упускаешь
vsb>Вопрос — чем заменить этот код? Пока написал как-то так (Java 7):
А чего этот код делает? Именованный лок, который много раз может лочиться и отпускаться? Нормально тогда.
Имеется ситуация, в которой оказалось удобно использовать код вида
synchronized (key.toString().intern()) {
...
}
но многие пишут, что так не стоит делать, это засорение intern-пула, это теоретический дедлок, если в другом месте случайно по той же строке будет синхронизация и тд. Вопрос — чем заменить этот код? Пока написал как-то так (Java 7):
но как-то это всё сложно выглядит. Не упускаю ли я какой-то готовый примитив синхронизации? Помимо прочего тут ещё и locks будет расти, в моём случае я её чищу сразу после synchronized, потому что у меня этот блок выполняется один раз, но в общем случае так нельзя делать, я сходу даже не соображу, как правильно это написать для общего случая.
Re[2]: Чем заменить synchronized (string.intern())
Здравствуйте, C0s, Вы писали:
C0s>Не знаю, можно ли другим примитивом, но если делать так, как написано, то тебе надо использовать computeIfAbsent, который для ConcurrentHashMap — атомарный, т.о. избавишься от громоздкости.
В общем, если кому надо, накидал такой код, вроде должен работать. Поискал альтернативы, по сути примерно так все делают, для оптимизации производительности можно держать не один глобальный лок на map, а список (выбирать по хешу ключа), но тогда надо вместо HashMap ConcurrentHashMap использовать.
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class LockMap {
interface Unlockable {
void unlock();
}
private final Lock locksLock = new ReentrantLock();
private final Map<Object, ReferenceCountingLock> locks = new HashMap<Object, ReferenceCountingLock>();
Unlockable lock(Object key) {
boolean lockSuccessful = false;
ReferenceCountingLock rcl = acquire(key);
try {
Unlocker unlocker = new Unlocker(key, rcl);
rcl.lock.lock();
lockSuccessful = true;
return unlocker;
} finally {
if (!lockSuccessful) {
release(key, rcl);
}
}
}
private ReferenceCountingLock acquire(Object key) {
locksLock.lock();
try {
ReferenceCountingLock rcl = locks.get(key);
if (rcl == null) {
rcl = new ReferenceCountingLock();
locks.put(key, rcl);
}
rcl.count++;
return rcl;
} finally {
locksLock.unlock();
}
}
private void release(Object key, ReferenceCountingLock rcl) {
locksLock.lock();
try {
rcl.count--;
if (rcl.count == 0) {
ReferenceCountingLock oldRCL = locks.remove(key);
if (rcl != oldRCL) {
throw new IllegalStateException();
}
}
} finally {
locksLock.unlock();
}
}
private static class ReferenceCountingLock {
private int count;
private final Lock lock = new ReentrantLock();
}
private class Unlocker implements Unlockable {
private final Object key;
private final ReferenceCountingLock rcl;
private Unlocker(Object key, ReferenceCountingLock rcl) {
this.key = key;
this.rcl = rcl;
}
@Override
public void unlock() {
try {
rcl.lock.unlock();
} finally {
release(key, rcl);
}
}
}
}