我是Java并发新手,我遇到了一个非常奇怪的问题: 我从一个大文件中读取并使用了几个工作线程来处理输入(一些复杂的字符串匹配任务)。我使用LinkedBlockingQueue将数据传输到工作线程,并使用worker类中的volatile布尔标志来在到达文件结尾时响应信号。
但是,我无法让工作线程正常停止。该程序的CPU使用率最终几乎为零,但程序不会正常终止。
简化代码如下。我删除了真正的代码,并用一个简单的字计数器替换它们。但效果是一样的。处理完整个文件后,工作线程不会终止,并且主线程中的布尔标志设置为true。
main
public class MultiThreadTestEntry
{
private static String inputFileLocation = "someFile";
private static int numbOfThread = 3;
public static void main(String[] args)
{
int i = 0;
Worker[] workers = new Worker[numbOfThread];
Scanner input = GetIO.getTextInput(inputFileLocation);
String temp = null;
ExecutorService es = Executors.newFixedThreadPool(numbOfThread);
LinkedBlockingQueue<String> dataQueue = new LinkedBlockingQueue<String>(1024);
for(i = 0 ; i < numbOfThread ; i ++)
{
workers[i] = new Worker(dataQueue);
workers[i].setIsDone(false);
es.execute(workers[i]);
}
try
{
while(input.hasNext())
{
temp = input.nextLine().trim();
dataQueue.put(temp);
}
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
input.close();
for(i = 0 ; i < numbOfThread ; i ++)
{
workers[i].setIsDone(true);
}
es.shutdown();
try
{
es.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
}
}
worker
班
public class Worker implements Runnable
{
private LinkedBlockingQueue<String> dataQueue = null;
private volatile boolean isDone = false;
public Worker(LinkedBlockingQueue<String> dataQueue)
{
this.dataQueue = dataQueue;
}
@Override
public void run()
{
String temp = null;
long count = 0;
System.out.println(Thread.currentThread().getName() + " running...");
try
{
while(!isDone || !dataQueue.isEmpty())
{
temp = dataQueue.take();
count = temp.length() + count;
if(count%1000 == 0)
{
System.out.println(Thread.currentThread().getName() + " : " + count);
}
}
System.out.println("Final result: " + Thread.currentThread().getName() + " : " + count);
}
catch (InterruptedException e)
{
}
}
public void setIsDone(boolean isDone)
{
this.isDone = isDone;
}
}
对于为什么会发生这种情况的任何建议?
非常感谢。
答案 0 :(得分:0)
由于Dan Getz已经说过你的工作人员take()
等待一个元素可用但是队列可能是空的。
在你的代码中,你检查Queue是否为空,但没有任何东西可以阻止其他Worker在检查后从元素中读取和删除元素。
如果Queue只包含一个元素,t1
和t2
是两个线程
可能发生以下情况:
t2.isEmpty(); // -> false t1.isEmpty(); // -> false t2.take(); // now the queue is empty t1.take(); // wait forever
在这种情况下,t1
会等待&#34;永远&#34;。
您可以使用poll
代替take
来避免这种情况,并检查结果是否为null
public void run()
{
String temp = null;
long count = 0;
System.out.println(Thread.currentThread().getName() + " running...");
try
{
while(!isDone || !dataQueue.isEmpty())
{
temp = dataQueue.poll(2, TimeUnit.SECONDS);
if (temp == null)
// re-check if this was really the last element
continue;
count = temp.length() + count;
if(count%1000 == 0)
{
System.out.println(Thread.currentThread().getName() + " : " + count);
}
}
System.out.println("Final result: " + Thread.currentThread().getName() + " : " + count);
}
catch (InterruptedException e)
{
// here it is important to restore the interrupted flag!
Thread.currentThread().interrupt();
}
}