Часто, при разработке многопоточного приложения, в
котором разделяемые ресурсы обрабатываются множеством потоков
одновременно, возникает задача корректного обеспечения доступа к ресурсу. Например, каждый в системе ресурс идентифицируется некоторым ключом. Необходимо обеспечить
действие над ним в один поток. В случае, когда ресурс один -
мы создаем ReentrantLock и блокируем бы доступ другим потокам, пока один поток выполняет действие:
package ru.egrik. striped ;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author echernyshev
* @since Aug 30, 2014
*/
public class StripedTest
{
private Object resource;
private Lock lock = new ReentrantLock();
public void runAction()
{
lock.lock();
try
{
actionWithResource(resource);
}
finally
{
lock.unlock();
}
}
private void actionWithResource(Object obj)
{
// do something
}
}
А если ресурсов стало много, то код будет выглядеть так:
package ru.egrik. striped;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author echernyshev
* @since Aug 30, 2014
*/
public class StripedTest
{
private Map<String, Object> resources;
private Lock lock = new ReentrantLock();
public void runAction(String key)
{
lock.lock();
try
{
actionWithResource(resources.get(key));
}
finally
{
lock.unlock();
}
}
private void actionWithResource(Object obj)
{
// do something
}
}
Когда ресурсов становится много,
использование одного lock'а не оправдано, так как это приводит к грубой
синхронизации к снижению производительности системы вцелом. Возникает
необходимость каждому ресурсу хранить свой lock. И если
количество ресурсов заранее не зафиксировано и постоянно меняется, то
при создании/удалении ресурса, необходимо создавать/удалять lock.
- java.util.concurrent.locks.Lock
- java.util.concurrent.locks.ReadWriteLock
- java.util.concurrent.Semaphore
Кроме того, для каждой из реализаций
предлагается вариант с lazy инициализацией и слабой (weak) ссылкой на
lock. То есть если ни один поток не использует lock, и ни один поток не
ожидает освобождения lock'а, то этот lock будет безопасно удален из
памяти сборщиком мусора. Данные реализации позволят снизить потребление
памяти, если в определенный момент времени будет использоваться только
несколько блокировок.
Используем lazyWeakLock в нашем классе:
package ru.egrik.striped;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import com.google.common.util.concurrent. Striped ;
/**
* @author echernyshev
* @since Aug 30, 2014
*/
public class StripedTest
{
private Map<String, Object> resources;
private Striped<Lock> striped = Striped.lazyWeakLock(2);
public void runAction(String key)
{
Lock lock = striped.get(key);
lock.lock();
try
{
actionWithResource(resources.get(key));
}
finally
{
lock.unlock();
}
}
private void actionWithResource(Object obj)
{
// do something
}
}
Спасибо за внимание!