2014-02-19 96 views
1

我正在为Java游戏编写一个Java程序。一个线程为Manager和Worker线程管理时间并为它们发送检测信号。 Manager收集并解释来自服务器的消息,并且Worker线程从Manager接收命令(具有类中的详细信息)并对此信息起作用。Java中的多线程死锁

现在,我遇到了多线程死锁问题,我不确定是否有替代方案。

以下是我的管理器类中的一些方法:当用户单击按钮时,我从GUI线程中调用getMessageFromUsername。以下方法从另一个线程中调用。

private ArrayList<Message> MessageList = new ArrayList<>(); 

public synchronized Message getMessageFromUsername(String username) {   
     for(Message msg : MessageList) { 
      if(msg.username.equalsIgnoreCase(username)) { 
       Message m = new Message(msg.num, msg.username, msg.id);     
       return m; 
      } 
     } 

     return null; 
    } 

我的管理器线程从套接字读取信息,并在连续循环中向MessageList添加信息。 (另外一个,即)

private synchronized void parseMessage() {} 
private void main() { while(1) { parseMessage(/*Adds message to MessageList*/); Sleep(); } } 

现在,我的问题是一个相当noobie发出─因为如果我要写信给我MessageList中必须使用同步访问。但是,由于这发生在一个循环中,并且消息不断通过套接字进入,它会导致死锁,因为它会一直锁定我的对象,并且这发生在循环中。

我该怎么做才能解决死锁问题?

+0

最简单的方法是在循环中添加一些暂停。你可以使用'Thread.sleep()' –

+0

我在我的循环中使用了100毫秒的暂停,但由于某种原因它仍然发生死锁。什么是暂停的好时机? – Jason

+3

错错了错! @ bali182请说你在拖他!睡眠怎样才能成为僵局的妥善解决方案?另一方面,你究竟在哪里看到了僵局?你在这里是一个饥饿的线程的例子。 '同步的'原语不适合你,你应该选择一些优先机制。 –

回答

1

正如我在评论中所说,你没有死锁,但你饿死你的线程。

你有两个选择:

  • 要么你去一些主题与优先排序

  • ,或者你去并发启用集合。 java.util.concurrent有很多这样的集合的例子。但要小心 - 修改一个被滥用的集合永远不是一个好主意。也许你只需要一种fifo队列?一个线程添加一条消息,另一个线程弹出并评估?

例如有ConcurrentLinkedQueue,提供Add方法和isEmpty()(由您parseMessage使用)(在一个while循环的其他线程的使用)。

虽然我不是Java专家,但我会指出您的答案:How to use ConcurrentLinkedQueue?它提供了一些实用建议。

+0

谢谢。我正在寻找线程饿死现在。看起来我错了,它是一个僵局。 – Jason

+0

请看看我的编辑和链接 –

+0

将优先级分配给线程时的一般经验法则:给出释放资源优先级的线程而非占用线程的线程。将消息添加到队列的线程正在消耗资源(内存)。从队列中移除消息并执行某些操作的线程(可能)在释放内存时释放内存。 –

2

我认为主要的问题是,你似乎在等待消息到达时保持锁定状态。

当您开始阅读邮件时,您可以通过parseMessage()进行锁定,邮件可能一段时间不会到达,另一个线程在消息到达之前无法做任何事情。然后它可能会错过它的机会......(等待/通知可能会解决问题的后半部分,但我认为这不是必需的)。

我会重新调整如下:

// just parse and return the message, don't add it to the list 
private Message parseMessage() {}  

private void main() { 
    while(1) { 
     Message msg = parseMessage(); 
     synchronized(messageList) { 
     messageList.add(msg); 
     } 
    } 
} 
+0

在我看来,虽然尽可能少地同步并且在任何IO上同步是一个坏主意,但值得一提的是,这不能解决饥饿。就像睡眠一样,它只是移动压力,并且(很大程度上)增加了线程交织的机会,但是从理论的角度来看并没有提供任何保证。在这种情况下它可能会有很大的帮助,但是;-) –

+0

那么你在改变的程序中看到了饥饿的可能性呢? –

1

如果我理解你的使用情况正确的话,我相信,我会考虑使用的BlockingQueue实现你的邮件列表。如前所述,还提供了并发实现,因此您不需要外部同步。您应该能够从队列中简单地输入poll()take()

0

我认为你应该使用ArrayBlockingQueue并避免使用循环,而是用诸如poll/take/put/offer之类的阻塞方法替换循环。

基于数组的集合表现更好,所以您应该先考虑这些基于链接节点的集合。所以ArrayBlockingQueue比ConcurrentLinkedQueue更好。