Java并发和死锁 - 多个消费者

时间:2015-07-24 16:43:57

标签: java multithreading deadlock

我创建了一个类,它尝试在多个线程中处理一些消息,其中每个消息都属于一个特定的组。每条消息都被添加到ConcurrentHashMap中,其中有一个密钥作为组号,并在这些线程处于“处理”状态时填满。我注意到它们有时并行运行,有时不运行。更糟糕的是,当有超过2个进程线程运行时,它们总是完全死锁。

[EDIT] ConcurrentHashMap的迭代在当时似乎是一种通过所有元素的好方法,因为编号的消息组(键)是未知的,并且它可能随时间而变化。该任务指定将所有消息组合在一起进行处理,但是当组中只有一条消息时,它仍应处理。所以我认为这是一种在元素到达时对元素进行排序的方法,而不知道哪些组存在。 [\EDIT]

public class GroupPriorityProcess implements Runnable {

private static final Object lock = new Object();
private static final Object counterLock = new Object();

private static int threadCounter = 0;
private final int currentThreadNumber;

private static Iterator<Integer> groupIterator;
private ConcurrentHashMap<Integer, LinkedBlockingQueue<Message>> groupMsgQueues;

public GroupPriorityProcess(ConcurrentHashMap<Integer, LinkedBlockingQueue<Message>> groupedMsgQueues) {
    groupMsgQueues = groupedMsgQueues;

    synchronized(lock){
        if (groupIterator == null)
        groupIterator = groupedMsgQueues.keySet().iterator();
    }
    synchronized (counterLock) {
        currentThreadNumber = (threadCounter++);
    }
}

// Main while loop for threads to process messages
public void run() {
    while (true) {
        LinkedBlockingQueue<Message> queue = chooseGroup();
        synchronized (queue) {
            process(queue);
        }
    }
}

// Loops till finds a message group available for processing.
private LinkedBlockingQueue<Message>  chooseGroup() {
    synchronized (lock) {
        while (!groupIterator.hasNext()) {
            groupIterator = groupMsgQueues.keySet().iterator();
        }
        LinkedBlockingQueue<Message> queue = groupMsgQueues.get(groupIterator.next());
        return queue;
    }
}

// takes messages from the a particular message group queue to completes the
// send process
private void process(LinkedBlockingQueue<Message> queue) {
    try {
        while (!queue.isEmpty()) {
            Message msg = queue.take();
            msg.appendMessage("Thread: " + currentThreadNumber);
            msg.completed();
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

[EDIT] 这些消息将添加到另一个类GatewayImp中。

public void send(Message msg) {
    int groupID = msg.getGroupID();

    if (groupedMsgQueues.containsKey(groupID)) {
        LinkedBlockingQueue<Message> queue = groupedMsgQueues.get(groupID);
        queue.add(msg);
    } else {
        LinkedBlockingQueue<Message> queue = new LinkedBlockingQueue<Message>();
        try {
            queue.put(msg);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        groupedMsgQueues.put(groupID, queue);
    }
}
}

我有几次使用'check then act',我认为需要在同步块中制作原子,但我想知道有更好的方法。非常感谢任何帮助,因为我只是刚开始学习并发性,而且我发现很难特别关注锁定。

1 个答案:

答案 0 :(得分:0)

我的第一个猜测是你不应该在queue方法的run上进行同步。

如果您稍后在LinkedBlockingQueue方法中调用queue.take()(同时仍保留process的互斥锁),则可能会与queue的内部同步发生冲突。

为了帮助您调试代码,添加详细日志记录(例如,一些System.out.println语句)通常很有用。好消息是你似乎有办法重现死锁。通常,这说起来容易做起来......