2009-11-27 2 views
2

У меня есть код, похожий на следующий:Синхронизация на двух или более объектов (Java)

public class Cache{ 
private final Object lock = new Object(); 
private HashMap<Integer, TreeMap<Long, Integer>> cache = 
    new HashMap<Integer, TreeMap<Long, Integer>>(); 
private AtomicLong FREESPACE = new AtomicLong(102400); 

private void putInCache(TreeMap<Long, Integer> tempMap, int fileNr){ 
    int length; //holds the length of data in tempMap 
    synchronized(lock){ 
    if(checkFreeSpace(length)){ 
    cache.get(fileNr).putAll(tmpMap); 
    FREESPACE.getAndAdd(-length); 
    } 
    } 
} 

private boolean checkFreeSpace(int length){  
    while(FREESPACE.get() < length && thereIsSomethingToDelete()){ 
    // deleteSomething returns the length of deleted data or 0 if 
    // it could not delete anything 
    FREESPACE.getAndAdd(deleteSomething(length)); 
    } 
    if(FREESPACE.get() < length) return true; 
    return false; 
} 
} 

putInCache называется около 139 потоков в секунду. Могу ли я быть уверенным, что эти два метода будут синхронизироваться как на cache, так и на FREESPACE? Кроме того, есть checkFreeSpace() многопоточный сейф. Могу ли я быть уверенным, что будет только один вызов этого метода за раз? Можно ли улучшить «многопоточность» этого кода?

+0

(Нет смысла использовать 'AtomicLong' в этом коде.) –

ответ

2

Обычно вы не синхронизируете поля, для которых вы хотите напрямую контролировать доступ. Поля, к которым вы хотите синхронизировать доступ, должны быть доступны только из синхронизированных блоков (на одном и том же объекте), которые считаются потокобезопасными. Вы уже делаете это в putInCache(). Поэтому, поскольку checkFreeSpace() получает доступ к общему состоянию несинхронизированным образом, он не является потокобезопасным.

+0

Что делать, если я объявляю' putInCache() 'как' synchronized'? Это сделает оба метода «более безопасными»? – Azimuth

+0

@Azimuth Вам необходимо защитить доступ на уровне поля, что означает, что все методы, которые обращаются к этим полям, должны синхронизироваться на одном и том же объекте блокировки. Короче говоря, вам нужно будет добавить синхронизированный блок в checkFreeSpace(). –

3

Чтобы полностью ответить на ваш вопрос, вам нужно будет показать реализации методов thereIsSomethingToDelete() и deleteSomething().

Учитывая, что checkFreeSpace является общедоступным методом (действительно ли он должен быть?) И несинхронизирован, возможно, он может быть вызван другим потоком, пока выполняется синхронизированный блок в методе putInCache(). Это само по себе не может сломать что-либо, так как кажется, что метод checkFreeSpace может только увеличить объем свободного места, а не уменьшать его.

Что было бы более серьезным (и образец кода не позволяет нам это определить), если методы thereIsSomethingToDelete() и deleteSomething() не правильно синхронизируют их доступ к объекту кеша, используя тот же объект блокировка, используемая putInCache().

+0

Спасибо за ваш ответ. Прежде всего, 'public' не является большой проблемой, потому что эти два метода вызываются только другим методом в том же классе. Что касается 'thereIsSomethingToDelete()' и 'deleteSomething()', у меня нет этого метода на самом деле. Их логика включена в метод 'checkFreeSpace', но это было бы слишком грязно, если бы я включил весь код здесь. Поэтому давайте просто предположим, что эти методы правильно синхронизированы. – Azimuth

+0

Извините, я выбрал неправильные методы, чтобы задавать вопросы. Пожалуйста, взгляните на отредактированную версию. – Azimuth

+0

Таким образом, предположительно putInCache() на самом деле не является приватным, то либо (иначе, как класс мог бы использоваться). Я думаю, что тогда класс является потокобезопасным, как описано, поскольку весь доступ к изменяемому состоянию возможен только в том случае, если поток содержит блокировку, установленную блоком синхронизации putInCache(). – Henry

Смежные вопросы