简体   繁体   English

Java并发和死锁-多个使用者

[英]Java Concurrency and Deadlocks - multiple consumers

I have created a class that attempts to process some messages in multiple threads, where each message belongs to a particular group. 我创建了一个试图处理多个线程中的某些消息的类,其中每个消息都属于一个特定的组。 Each message is being added to the ConcurrentHashMap which has a key as the group number and is filling up while these threads are "processing". 每个消息都将添加到ConcurrentHashMap中,该消息具有一个键作为组号,并且在这些线程正在“处理”时正在填充。 I have noticed that they sometimes run in parallel and sometimes not. 我注意到它们有时并行运行,有时却不并行。 And to make matters worse when there are more than 2 process threads running always they deadlock entirely. 更糟糕的是,当有两个以上的进程线程始终在运行时,它们将完全死锁。

[EDIT] Iteration of the ConcurrentHashMap seemed at the time to be a good way of going through all the elements as the numbered message groups (keys) are not known and it could change over time. [EDIT]当时,对ConcurrentHashMap迭代似乎是遍历所有元素的一种好方法,因为编号的消息组(键)尚不清楚,并且可能会随着时间而变化。 The task specified that all messages be grouped together for processing but when when there is only one message in a group it should still process. 该任务指定将所有消息分组在一起进行处理,但是当组中只有一条消息时,仍应处理。 So I thought this was a way to sort the elements as they arrive without knowing at the beginning which groups exist. 因此,我认为这是一种在元素到达时对其进行排序的方法,而无需一开始就知道存在哪些组。 [\\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] The messages are added here in another class GatewayImp . [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);
    }
}
}

I have a few times when I use 'check then act' which I thought needs to be in a synchronized block to make atomic but I would like to know there is some better way. 我有几次使用“先检查后行动”的方法,我认为需要在同步块中使它原子化,但是我想知道有更好的方法。 Please any help with this is greatly appreciated as I am only just starting to learn about concurrency and I'm finding it hard to get my head around locking in particular. 请对此提供任何帮助,我们将不胜感激,因为我才刚刚开始了解并发性,而且我发现很难专一锁定。

My first guess would be that you should not synchronize on the queue in your run method. 我的第一个猜测是您不应该在run方法中的queue进行同步。

It can collide with the internal synchronization of LinkedBlockingQueue , when you call queue.take() later in the process method (while still holding the mutex of queue ). 它可与内部同步发生碰撞LinkedBlockingQueue ,当调用queue.take()在后面的process方法(同时仍保持的互斥queue )。

To help you debug your code, it is often useful to add verbose logging (eg, some System.out.println statements). 为了帮助您调试代码,添加冗长的日志记录(例如某些System.out.println语句)通常很有用。 The good news is that you seem to be handle to reproduce the deadlock. 好消息是您似乎可以重现死锁。 Often, this is easier said than done... 通常,这说起来容易做起来难。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM