问题解决了!感谢你们。请在下面自己查看答案
==================
在我写的一个方法中:
while (queueP.isEmpty()) ;
queueP是一个LinkedList,还有另一个线程试图锁定它并写入它。我想确保queueP先不为空,然后再去。但代码最终成为一个死循环。 jstack显示这个while循环锁定了queueP所以写入线程总是等待锁定它,并且没有任何东西写入queueP并且循环永远不会结束。
这是jstack结果中的内容:
"c2-0" #11 prio=5 os_prio=0 tid=0x000000001d51b800 nid=0x46a0 runnable [0x000000001de4e000]
java.lang.Thread.State: RUNNABLE
at dbq.DoubleBufferQueue.take(DoubleBufferQueue.java:44)
- locked <0x000000076f2083b8> (a java.util.LinkedList)
at dbq.Test$ToyConsumer.run(Test.java:91)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None
"p2-0" #10 prio=5 os_prio=0 tid=0x000000001d519800 nid=0xbc4 waiting for monitor entry [0x000000001dd4e000]
java.lang.Thread.State: BLOCKED (on object monitor)
at dbq.DoubleBufferQueue.put(DoubleBufferQueue.java:33)
- waiting to lock <0x000000076f2083b8> (a java.util.LinkedList)
at dbq.Test$ToyProducer.run(Test.java:58)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None
DoubleBufferQueue.java:44是while循环。
那么,为什么这个列表在while循环中被锁定?它是JVM的某种功能吗?
=============================================== =======
put()
和take()
是:
@Override
public void put(T e) throws InterruptedException {
// TODO Auto-generated method stub
synchronized (queueP) {
queueP.offer(e);
}
}
@Override
public T take() throws InterruptedException {
// TODO Auto-generated method stub
synchronized (queueC) {
if (queueC.isEmpty()) {
while (queueP.isEmpty()) ;
synchronized (queueP) {
Queue<T> tmp = queueC;
queueC = queueP;
queueP = tmp;
}
}
return queueC.poll();
}
}
我今天读到了关于双缓冲区队列的问题,并且我试图自己编写一个(有人可以告诉我这是否是正确的名称?)。这只是一个简单的演示。我知道take()
中的切换看起来非常危险,而且ReentrantLock会好得多,但我只是好奇......所以我只创建了一个消费者和一个生产者,但问题仍然存在。
jstack是否显示列表锁定的确切行?我对此并不了解。第44行只是while循环。
答案 0 :(得分:3)
请注意,此实施未同步。如果多个线程同时访问链表,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。
如果没有同步,则无法保证在输入循环后将重新检查queueP.isEmpty()
的值。
正如@Mena建议的那样,您应该在java.util.concurrent
包中查看专为并发访问而设计的集合。
答案 1 :(得分:0)
现在我知道为什么它有时会陷入死循环。
关键是,尝试锁定对象的第一个线程不一定首先获得锁定。只有当两个操作符合&#34; Happens-Before&时才会重新排序。 #34;关系。
所以这就是在死循环开始之前发生的事情,给定一个消费者线程&#34; c2-0&#34;和生产者线程&#34; p2-0&#34;。
queueC = queueP
之后和queueP = tmp;
之前被暂停。put()
试图锁定queueP,暂停。take()
的此次通话。很快c2-0开始下一个taken()
,试图锁定队列C,这也是p2-0所需要的,因为当p2-0卡住时,queueC和queueP都引用相同的LinkedList。我从中学到了什么:
while(queueP.isEmpty())
锁定了列表。事实上它的&#39;结果说&#34;线程c2-0锁定队列P并且它现在在第44行运行&#34;这完全是无关紧要的。真是个误会......