简体   繁体   English

static volatile boolean - 线程没有被终止

[英]static volatile boolean - thread not getting terminated

I wrote simple multithreaded application, just to play around with concurrency but I have a problem with boolean variable which controles the loop in thread. 我编写了简单的多线程应用程序,只是为了解决并发问题,但我遇到了一个boolean变量的问题,它控制了线程中的循环。 One of the functions should stop the thread if there's noelements left in queue and I guess that is my problem because If I add something in between braces to: 其中一个函数应该停止线程,如果队列中没有留下任何元素,我想这是我的问题,因为如果我在大括号之间添加一些内容:

while (!queue.isEmpty()) {
}
isRunning = false;

So it becomes : 所以它变成:

while (!queue.isEmpty()) {
    System.out.println("ASD");
}
isRunning = false;

It is working a bit better - the program terminates after executing turnOff method 它工作得更好 - 程序在执行turnOff方法后终止

Any Ideas? 有任何想法吗?

Here is full code of my app: 这是我的应用程序的完整代码:

package test;

public class xxx {
    public static void main(String[] args) {
        Foo instance = Foo.getInstance();
        Thread x = new Thread(instance);
        x.start();

        for (int count = 1; count < 100000; count++)
            instance.addToQueue(count + "");
        instance.turnOff();
    }
}

And: 和:

package test;

import java.util.LinkedList;
import java.util.List;

public class Foo implements Runnable {
    private static Foo inner = null;
    private static List<String> queue = new LinkedList<String>();
    private volatile static boolean isRunning = false;

    private Foo() { }

    public static Foo getInstance() {
        if (inner == null) {
            inner = new Foo();
        }
        return inner;
    }

    public void addToQueue(String toPrint) {
        synchronized (queue) {
            queue.add(toPrint);
        }

    }

    public void removeFromQueue(String toRemove) {
        synchronized (queue) {
            queue.remove(toRemove);
        }
    }

    public void turnOff() {
        while (!queue.isEmpty()) {
        }
        System.out.println("end");
        isRunning = false;
    }

    @Override
    public void run() {
        isRunning = true;
        while (isRunning) {
            if (!queue.isEmpty()) {
                String string = queue.get(0);
                System.out.println(string);
                removeFromQueue(string);
            }

        }
    }
}

It is a race condition problem. 这是一个竞争条件问题。 Possibly the run method (the other thread) is executed after the turnOff in in the main thread so the flag isRunning is set as true again and the loop never ends. 可能在主线程中的turnOff输入之后执行run方法(另一个线程),因此标志isRunning再次设置为true并且循环永远不会结束。

That would explain why with a simple System.out.println("ASD") becomes better: the isRunning=false is delayed. 这可以解释为什么使用简单的System.out.println(“ASD”)变得更好:isRunning = false被延迟。

You have lots of problems in your code. 您的代码中存在很多问题。

  1. Busy loops in turnOff and wait 在繁忙的环路turnOffwait
  2. Unsynchronized access to queue in turnOff and run turnOffrunqueueturnOff同步访问
  3. Non-volatile, non-final access to inner 非易失性,非最终访问inner
  4. Needlessly static isRunning and queue variables 毋静态isRunningqueue变量
  5. Race condition between turnOff and start invocations turnOffstart调用之间的竞争条件

Some of these are harmless in this specific instance (eg instance is always accessed from the main thread), but depending on your hardware configuration you are going to get bitten by some combination of the rest of them. 其中一些在这个特定的实例中是无害的(例如, instance总是从主线程访问),但是根据您的硬件配置,您将被其余部分的某些组合所咬。 The reason that adding the System.out "fixes" the problem is that it renders one of the busy loops less busy (fixes 1) and has an internal synchronization mechanism (fixes 2), but the others are still there. 添加System.out “修复”问题的原因是它使其中一个繁忙的循环不那么忙(修复1)并且具有内部同步机制(修复2),但其他循环仍在那里。

I suggest getting rid of the isRunning variable and the test for queue.isEmpty() and replacing with a CountDownLatch . 我建议删除isRunning变量和queue.isEmpty()测试并替换为CountDownLatch

package test;

import java.util.LinkedList;
import java.util.List; 
import java.util.concurrent.CountDownLatch;

public class Foo implements Runnable {
    private static final Foo inner = new Foo();
    private final List<String> queue = new LinkedList<String>();
    private final CountDownLatch latch = new CountDownLatch(1);

    private Foo() { }

    public static Foo getInstance() {
        return inner;
    }

    public void addToQueue(String toPrint) {
        synchronized (queue) {
            queue.add(toPrint);
        }
    }

    public void removeFromQueue(String toRemove) {
        synchronized (queue) {
            queue.remove(toRemove);
        }
    }

    public boolean isEmpty() {
        synchronized (queue) {
            return queue.isEmpty();
        }
    }

    public String getHead() {
        synchronized (queue) {
            return queue.get(0);
        }
    }

    public void turnOff() throws InterruptedException {
        latch.await();
        System.out.println("end");
    }

    @Override
    public void run() {
        while (!isEmpty()) {
            String string = getHead();
            System.out.println(string);
            removeFromQueue(string);
        }

        latch.countDown();
    }
}

And the runner 和跑步者

package test;

public class XXX {
    public static void main(String[] args) throws InterruptedException {
        Foo instance = Foo.getInstance();
        Thread x = new Thread(instance);

        for (int count = 1; count < 100000; count++)
            instance.addToQueue(count + "");

        x.start();
        instance.turnOff();
    }
}   

The main problem is the race condition between adding/removing elements and checking whether the queue is empty. 主要问题是添加/删除元素和检查队列是否为空之间的竞争条件。 In more words: 换句话说:

Wrapping add and remove calls in synchronized block provides you guarantees that all invocations of these methods will be performed sequentially. synchronized块中包装addremove调用可以保证将按顺序执行这些方法的所有调用。 But, there is one more access to queue variable outside of synchronized block - it is queue.isEmpty() . 但是,还有一个对synchronized块之外的queue变量的访问 - 它是queue.isEmpty() It means there is a chance that some thread will get the result of this call and while it performs actions inside if block, other thread may add or remove elements. 这意味着某个线程有可能获得此调用的结果,并且当它在if块内执行操作if ,其他线程可能会添加或删除元素。

This code also has some more concurrency problems, please let me know if you want them to be discussed (they are a little bit offtopic). 此代码还有一些更多的并发问题,如果您想要讨论它们,请告诉我们(它们有点偏离主题)。

As Germann Arlington point, the value of queue.isEmpty() seems to be cached in the main thread. 正如Germann Arlington指出的那样,queue.isEmpty()的值似乎被缓存在主线程中。 Try synchronize it: 尝试同步它:

while (true) {
    synchronized(queue) {
        if(queue.isEmpty())
            break;
    }
} 

Or just make the queue to be volatile: 或者只是让队列变得不稳定:

private volatile static List<String> queue = new LinkedList<String>();

This will solve your problem. 这将解决您的问题。

Use volatile variable isRunning in turnOff() method's while loop also. 在turnOff()方法的while循环中也使用volatile变量isRunning。

public void turnOff() {
    while (isRunning && !queue.isEmpty()) {
    }
    System.out.println("end");
    isRunning = false;
}

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

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