高效实施独占执行

时间:2010-12-26 12:41:59

标签: java multithreading

我有一个用于处理付款的ObjectManager类。它包含在Order实体上,因此在需要处理时必须创建新实例。当几个ObjectManager实例同时处理相同的订单时,我需要防止这种情况(它由于远程支付处理中心的一些错误而发生一次,不知何故他们称我们的回调网址两次)。我想知道如何更有效地实施它。

现在,我正在考虑这样的事情:

public class OrderManager{

private static final CopyOnWriteArrayList<Integer> LOCKER = 
        new CopyOnWriteArrayList<Integer>();

private static synchronized boolean tryLock(Integer key) {
    return LOCKER.addIfAbsent(key);
}

private static void releaseLock(Integer key) {
    LOCKER.remove(key);
}

public void processPayment(Integer orderId) throws Exception{
  if (!tryLock(orderId)) {
    return;
  }
  try {
      //operate
  } finally {
      releaseLock(orderId);
  }
} 

//remainder omitted

}

3 个答案:

答案 0 :(得分:1)

使用CopyOnWriteArrayList对于读取来说是便宜的,但对于写入来说很昂贵,因为你似乎主要是写入,我建议使用O(1)进行写入的集合。

e.g。

private static final Set<Integer> LOCKER = Collections.newSetFromMap(
    new ConcurrentHashMap<Integer, Boolean>());

private static boolean tryLock(Integer key) {
    return LOCKER.add(key);
}

private static void releaseLock(Integer key) {
    LOCKER.remove(key);
}

答案 1 :(得分:1)

我最近实现了一个通用的多锁处理程序机制,可能适合你。

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

    /**
     * A synchronization utility for improving lock granularity.
     * 
     * The class is suitable when some operation is associated with an id, and we don't want different
     * threads to perform the operation on the same id concurrently. Concurrent executions on different ids is ok.
     * 
     * @param K - the type of the id
     *  
     * @author eschneider
     */
    public class MultiLock <K>{
        private ConcurrentHashMap<K, ReentrantLock> locks = new ConcurrentHashMap<K, ReentrantLock>();  

        /**
         * Locks on a given id.
         * Make sure to call unlock() afterwards, otherwise serious bugs may occur.
         * It is strongly recommended to use try{ }finally{} in order to guarantee this.
         * Note that the lock is re-entrant. 
         * @param id The id to lock on
         */
        public void lock(K id) {
            while (true) {
                ReentrantLock lock = getLockFor(id);
                lock.lock();
                if (locks.get(id) == lock)
                    return;
                else // means that the lock has been removed from the map by another thread, so it is not safe to
                     // continue with the one we have, and we must retry.
                     // without this, another thread may create a new lock for the same id, and then work on it.
                    lock.unlock();
            }
        }

        /**
         * Tries locking on a given id, giving up if somebody else has the lock.
         * Make sure to call unlock() later in case that the lock is acquired, otherwise serious bugs may occur.
         * It is strongly recommended to use try{ }finally{} in order to guarantee this. Note that the lock is re-entrant. 
         * @param id The id to lock on
         * @return true iff the lock has been acquired
         */
        public boolean tryLock(K id) {
            while (true) {
                ReentrantLock lock = getLockFor(id);
                if (!lock.tryLock())
                    return false;
                if (locks.get(id) == lock)
                    return true;
                else // means that the lock has been removed from the map by another thread, so it is not safe to
                     // continue with the one we have, and we must retry.
                     // without this, another thread may create a new lock for the same id, and then work on it.
                    lock.unlock();
            }
        }

        /**
         * Unlocks on a given id.
         * If the lock is not currently held, an exception is thrown.
         *  
         * @param id The id to unlock
         * @throws IllegalMonitorStateException in case that the thread doesn't hold the lock
         */
        public void unlock(K id) {
            ReentrantLock lock = locks.get(id);
            if (lock == null || !lock.isHeldByCurrentThread())
                throw new IllegalMonitorStateException("Lock for " + id + " is not owned by the current thread!");
            locks.remove(id);
            lock.unlock();
        }

        /**
         * Gets/creates a lock for a given id. If many threads try it on the same time,
         * they will all get the same lock.
         * @param id The id
         * @return The lock corresponding to the given id
         */
        private ReentrantLock getLockFor(K id) {
            ReentrantLock lock = locks.get(id);
            if (lock == null) {
                lock = new ReentrantLock();
                ReentrantLock prevLock = locks.putIfAbsent(id, lock);
                if (prevLock != null)
                    lock = prevLock;
            }
            return lock;
        }
    }

答案 2 :(得分:0)

我想我会在数据库级别实现锁定 - 所以如果ObjectManager在多个进程中运行,它仍然会识别锁定。