Java线程没有响应volatile boolean标志

时间:2015-05-18 01:17:07

标签: java multithreading

我是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;
    }
}

对于为什么会发生这种情况的任何建议?

非常感谢。

1 个答案:

答案 0 :(得分:0)

由于Dan Getz已经说过你的工作人员take()等待一个元素可用但是队列可能是空的。

在你的代码中,你检查Queue是否为空,但没有任何东西可以阻止其他Worker在检查后从元素中读取和删除元素。

如果Queue只包含一个元素,t1t2是两个线程 可能发生以下情况:

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();
    }
}