基于资源ID的锁定管理器中的竞争条件

时间:2020-06-26 12:37:35

标签: java multithreading locking

我正在尝试编写一个锁定管理器,它将从多个线程中调用。该管理器根据各种资源ID处理锁定。这些可能有很大的差异,因此在每个内存中保持锁定可能会导致大量内存使用。这就是为什么在不再使用锁(使用该锁的线程数达到0)之后将其从内存中删除的原因。

它可以根据请求的资源ID独占锁定线程(如果两个线程锁定相同的ID,一个线程将等待另一个线程对其进行解锁),或者使用ReentrantReadWriteLock完全排除所有其他线程。

我正在经历一种竞争状况,即当持有锁的最后一个线程将其解锁时,会从内存中删除一个锁,但其他线程仍尝试解锁吗?这导致了我无法解释的NPE。

我尝试使用AtomicInteger代替当前的volatile变量,认为它可能与此有关,但结果相似。

这是有问题的课程:

/**
 * This class provides locks for reading and writing, and bulk operations lock on the entire class.
 * 
 * If a bulk operation is not in progress, class based locking is transparent.
 * @author KiralyCraft
 *
 */
public class ReadWriteHighLevelLocking
{
    private class Semaphore
    {
        private ReentrantLock lock;
        private volatile int acquiredLocks;
        public Semaphore()
        {
            this.acquiredLocks = 0;
            this.lock = new ReentrantLock();
        }
        
        public synchronized int incrementAndGet()
        {
            return ++acquiredLocks;
        }
        
        public synchronized int decrementAndGet()
        {
            return --acquiredLocks;
        }
    }
    
    private ReentrantReadWriteLock classBasedLock;
    private volatile HashMap<String, Semaphore> stateChangeLocks;
    
    public ReadWriteHighLevelLocking()
    {
        this.stateChangeLocks = new HashMap<String,Semaphore>();
        this.classBasedLock = new ReentrantReadWriteLock();
    }
    
    /**
     * Acquires a lock for the specified resource ID.
     * 
     * May block if another thread is currently holding a bulk lock.
     * @param resourceID
     */
    public void acquireLock(String resourceID)
    {
        classBasedLock.readLock().lock(); //Using it reversed. There can be any number of operations (using the read lock), but only one bulk operation (sacrifice)
        Semaphore stateChangeLock;
        synchronized(stateChangeLocks)
        {
            if ((stateChangeLock = stateChangeLocks.get(resourceID))==null)
            {
                stateChangeLocks.put(resourceID, (stateChangeLock = new Semaphore()));
            }
        }
        stateChangeLock.lock.lock();
        stateChangeLock.incrementAndGet();
    }
    public void releaseLock(String resourceID)
    {
        Semaphore stateChangeLock;
        synchronized(stateChangeLocks)
        {
            stateChangeLock = stateChangeLocks.get(resourceID);
            if (stateChangeLock.decrementAndGet() == 0)  //<----------------- HERE IS THE NPE
            {
                stateChangeLocks.remove(resourceID);
            }
        }
        stateChangeLock.lock.unlock();
        classBasedLock.readLock().unlock();
    }
    
    /**
     * When a bulk lock is acquired, all other operations are delayed until this one is released.
     */
    public void acquireBulkLock()
    {
        classBasedLock.writeLock().lock();  //Using it reversed. There can be any number of writers (using the read lock), but only one reader (sacrifice)
    }
    
    public void releaseBulkLock()
    {
        classBasedLock.writeLock().unlock(); 
    }
}

呼叫方示例:

public abstract class AbstractDatabaseLockingController
{
    ...
    private ReadWriteHighLevelLocking highLevelLock;
    
    public AbstractDatabaseLockingController(DatabaseInterface db)
    {
        this.db = db;
        this.highLevelLock = new ReadWriteHighLevelLocking();
    }   
    ...
    public <T extends DatabaseIdentifiable> boolean executeSynchronizedUpdate(T theEntity,AbstractSynchronousOperation<T> aso)
    {
        boolean toReturn;
        String lockID = theEntity.getId()+theEntity.getClass().getSimpleName();
        highLevelLock.acquireLock(lockID);
        toReturn = aso.execute(theEntity,db);
        highLevelLock.releaseLock(lockID);
        return toReturn;        
    }
    ...
    public <T extends DatabaseIdentifiable> List<T> executeSynchronizedGetAllWhereFetch(Class<T> objectType, DatabaseQuerySupplier<T> dqs)
    {
        List<T> toReturn;
        highLevelLock.acquireBulkLock();
        toReturn = db.getAllWhere(objectType, dqs);
        highLevelLock.releaseBulkLock();
        return toReturn;
    }
}

注意:使用这种锁定管理器的所有位置都遵循样本类的获取/释放模式。基本上是唯一使用它的地方。其他线程可以通过示例类的子级间接调用上述方法

1 个答案:

答案 0 :(得分:0)

我似乎通过更新以下代码解决了该问题:

    synchronized(stateChangeLocks)
    {
        if ((stateChangeLock = stateChangeLocks.get(resourceID))==null)
        {
            stateChangeLocks.put(resourceID, (stateChangeLock = new Semaphore()));
        }
    }
    stateChangeLock.lock.lock();
    stateChangeLock.incrementAndGet();

    synchronized(stateChangeLocks)
    {
        if ((stateChangeLock = stateChangeLocks.get(resourceID))==null)
        {
            stateChangeLocks.put(resourceID, (stateChangeLock = new Semaphore()));
        }
        stateChangeLock.incrementAndGet();
    }
    stateChangeLock.lock.lock();