Ищем подходящий механизм синхронизации - PullRequest
1 голос
/ 01 июня 2019

Предположим, у меня есть следующий (псевдо) код:

class Cache {
    Entry addIfMissing(String data) {
        // omitted for brevity
    }
    void evictOldEntries() {
        // omitted for brevity
    }
}
class Program {
    private Cache cache = new Cache();

    doWork() { // called from multiple threads
        var entry = cache.addIfMissing("omitted for brevity");
        // work with entry
    }

    static {
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
            cache.evictOldEntries();
        }, 10, 10, TimeUnit.MINUTES);
    }
}

Я хочу убедиться, что во время работы метода evictOldEntries все остальные потоки в программе должны ждать, пока это будет сделано.

Какой механизм синхронизации подойдет для такого сценария?

Ответы [ 3 ]

3 голосов
/ 01 июня 2019

Что вам нужно, это что-то вроде этого:

class Cache {

    final ReentrantLock lock;

    public Cache { lock = new ReentrantLock(); }

    Entry addIfMissing(String data) {
        lock.lock();
        try {
            // Add data here
        }
        finally {
            lock.unlock();
        }
    }

    void evictOldEntries() {
        if (lock.tryLock()) {
           try {
              // Evict old entries
           }
           finally {
              lock.unlock();
           }
        }
    }
}
1 голос
/ 02 июня 2019

Я думаю, ReentrantReadWriteLock - это как раз то, что мне нужно:

class Program {
    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

    void doWork() { // called from multiple threads
        rwl.readLock().lock();
        try {
            var entry = cache.addIfMissing("omitted for brevity");
            // work with entry
        } finally {
            rwl.readLock().unlock();
        }
    }

    static {
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
            rwl.writeLock().lock();
            try {
                cache.evictOldEntries();
            } finally {
                rwl.writeLock().unlock();
            }
        }, 10, 10, TimeUnit.MINUTES);
    }
}

Таким образом, после записи writeLock doWork приходится блокировать, и наоборот.

1 голос
/ 02 июня 2019

Здесь нужно немного свободы, поскольку ваш пример кода и точные требования немного неясны. Но может ли что-то подобное работать? ConcurrentHashMap использует детальную блокировку, поэтому вы минимизируете узкое место при вставке записей в кеш. Поток эвитора может даже работать одновременно с потоками вставки.

class Cache<String, CacheEntry> {
    ConcurrentHashMap<String, CacheEntry> map = new ConcurrentHashMap<String, CacheEntry>();

    Entry addIfMissing(String data) {
        map.computeIfAbsent(...);
    }

    void evictOldEntries() {
        Iterator<Map.Entry<String, CacheEntry>> iterator = map.entrySet().iterator();

        while (iterator.hasNext()) {
            CacheEntry entry = iterator.next().getValue();

            if (shouldEvict(entry)) {
                iterator.remove();
            }
        }
    }
}
...