在集中的地方锁定交易

时间:2017-10-30 09:41:04

标签: java multithreading concurrency

我有以下情况:


public class LockingScenario {

    public static final ConcurrentHashMap<String, Vector<Task>> BUFFER = new ConcurrentHashMap<>();
    private static final ConcurrentHashMap<String, Object> LOCKS = new ConcurrentHashMap<>();
    private static final Object SURROGATE = new Object();

    public void bufferTransaction(Context ctx) {

        String transaction = ctx.getTransaction();

        Object obj = LOCKS.putIfAbsent(transaction, SURROGATE);
        Vector<Task> tasks;
        Iterator<String> it = LOCKS.keySet().iterator();

        while (it.hasNext()) {
            String key = it.next();
            if (key.equals(transaction)) {
                transaction = key;
            }
        }

        //minimize the case when there will be created useless Vector objects.
        if (obj == null) {
            tasks = new Vector<>();
            BUFFERS.putIfAbsent(transaction, tasks);
        }

        syncrhonized(transaction) {
            tasks = BUFFERS.get(transaction);
        }
        //... create task
        tasks.add(task);
        // ... 

    }

    public void finishTransaction(Context ctx) {
        String transaction = ctx.getTransaction();
        Vector<Task> tasks = BUFFER.get(transaction);
        // finish job here...
    }
}

我试图在这里解释一下上面的代码及其背景:

LockingScenario是托管bean的类,由不同的线程访问,它启动不同的事务。每个事务都有一个唯一的编号和许多与之关联的任务。

我遇到以下问题:我需要锁定一部分代码,这些代码在多个线程之间共享一些内存,为许多事务发送许多任务,这些任务必须是由托管bean处理(这个bean实际上可以有多个托管实例)

问题:这种情况有更好的方法吗?这假设我的方法是正确的(我的意思是它没有错误)。

重要提示:在上述方案中,您应该只考虑bufferTransaction及其上方的代码。下面的代码实际上是在一个不同的托管bean中。我把它放在那里让你全面了解。

2 个答案:

答案 0 :(得分:2)

我很确定你可以这样做:

class LockingScenario {
    private static final Map<String, Vector<Task>> BUFFER = new ConcurrentHashMap<>();

    public void bufferTransaction(Context ctx) {
        String transaction = ctx.getTransaction();
        Vector<Task> tasks;
        // prevent creation of two Vectors for the same transaction
        synchronized (BUFFER) {
            tasks = BUFFER.get(transaction);
            if (tasks == null) {
                tasks = new Vector<>();
                BUFFER.put(transaction, tasks);
            }
        }
        // create tasks and put into Vector
        tasks.add(createTask());
    }
}

任务列表检索/创建足够快,您可以阻止整个缓冲区。

使用Java 8,您可以使用

synchronized (BUFFER) {
    tasks = BUFFER.computeIfAbsent(transaction, s -> new Vector<>());
}

答案 1 :(得分:0)

虽然@ daniu的答案很好,但我只想指出OP中的代码包含竞争条件。

在轰鸣声片段中:


    while (it.hasNext()) {
        String key = it.next();
        if (key.equals(transaction)) {
            transaction = key;
        }
    }

    //minimize the case when there will be created useless Vector objects.
    if (obj == null) { // point 1
        tasks = new Vector();
        BUFFERS.putIfAbsent(transaction, tasks);
    }

    syncrhonized(transaction) { // point 2
        tasks = BUFFERS.get(transaction);
    }
    //... create task
    tasks.add(task); // point 3

你可以看到point 3point 1的竞争条件而出现不可预测的行为。在线程(让我们称之为线程A)尝试初始化 tasks Vector之前,另一个线程可以继续前进并在线程A可以在point 2获取锁定这样做,第二次访问所谓的创建Vector,可以创建一半或根本不创建。