简体   繁体   English

Java线程未响应易失性布尔标志

[英]Java thread not responding to volatile boolean flag

I am new to Java concurrency, and I met a very strange problem: I read from a large file and used several worker threads to work on the input (some complicated string matching tasks). 我是Java并发的新手,我遇到了一个非常奇怪的问题:我从一个大文件中读取并使用了多个工作线程来处理输入(一些复杂的字符串匹配任务)。 I used a LinkedBlockingQueue to transmit the data to the worker threads, and a volatile boolean flag in the worker class to respond to the signal when the end-of-file is reached. 我使用了LinkedBlockingQueue将数据传输到工作线程,并使用了worker类中的易失性布尔标志来在到达文件结尾时响应信号。

However, I cannot get the worker thread to stop properly. 但是,我无法使工作线程正常停止。 The CPU usage by this program is almost zero in the end, but the program won't terminate normally. 最后,该程序的CPU使用率几乎为零,但该程序不会正常终止。

The simplified code is below. 简化的代码如下。 I have removed the real code and replaced them with a simple word counter. 我删除了真实的代码,并用一个简单的单词计数器替换了它们。 But the effect is the same. 但是效果是一样的。 The worker thread won't terminate after the whole file is processed, and the boolean flag is set to true in the main thread. 在处理了整个文件之后,辅助线程将不会终止,并且在主线程中将boolean标志设置为true。

The class with main 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();
        }
    }
}

The worker class 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;
    }
}

Any suggestions to why this happens? 有什么建议为什么会发生这种情况?

Thank you very much. 非常感谢你。

As Dan Getz already said your worker take() waits until an element becomes available but the Queue may be empty. 正如Dan Getz所说的那样,您的worker take()会等到某个元素变为可用,但Queue可能为空。

In your code you check if the Queue is empty but nothing prevents the other Workers to read and remove an element from the element after the check. 在您的代码中,您检查队列是否为空,但是在检查之后,没有什么阻止其他Worker读取并从该元素中删除该元素。

If the Queue contains only one element and t1 and t2 are two Threads the following could happen: 如果队列仅包含一个元素,并且t1t2是两个线程,则可能会发生以下情况:

t2.isEmpty(); // -> false
t1.isEmpty(); // -> false
t2.take(); // now the queue is empty 
t1.take(); // wait forever

in this case t1 would wait "forever". 在这种情况下, t1将“永远”等待。

You can avoid this by using poll instead of take and check if the result is null 您可以通过使用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();
    }
}

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

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