static volatile boolean - 线程没有被终止

时间:2014-04-02 14:19:13

标签: java multithreading concurrency

我编写了简单的多线程应用程序,只是为了解决并发问题,但我遇到了一个布尔变量的问题,它控制了线程中的循环。其中一个函数应该停止线程,如果队列中没有留下任何元素,我想这是我的问题,因为如果我在大括号之间添加一些东西:

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

所以它变成了:

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

它工作得更好 - 程序在执行turnOff方法

后终止

任何想法?

以下是我的应用的完整代码:

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

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

        }
    }
}

5 个答案:

答案 0 :(得分:5)

这是一个竞争条件问题。可能在主线程中的turnOff输入之后执行run方法(另一个线程),因此标志isRunning再次设置为true并且循环永远不会结束。

这可以解释为什么使用简单的System.out.println(“ASD”)变得更好:isRunning = false被延迟。

答案 1 :(得分:3)

您的代码中存在很多问题。

  1. turnOffwait
  2. 中的繁忙循环
  3. queueturnOff
  4. run的非同步访问权限
  5. inner
  6. 的非易失性,非最终访问权限
  7. 不必要的静态isRunningqueue变量
  8. turnOffstart次调用之间的竞争条件
  9. 其中一些在这个特定实例中是无害的(例如,总是从主线程访问instance),但是根据您的硬件配置,您将被其余部分的某些组合所困扰。添加System.out“修复”问题的原因是它使其中一个繁忙的循环不那么忙(修复1)并且具有内部同步机制(修复2),但其他循环仍在那里。

    我建议删除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();
        }
    }
    

    和跑步者

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

答案 2 :(得分:1)

主要问题是添加/删除元素和检查队列是否为空之间的竞争条件。换句话说:

add块中包含removesynchronized调用可以保证所有这些方法的调用都会按顺序执行。但是,在queue块之外还有一个synchronized变量的访问权限 - 它是queue.isEmpty()。这意味着某个线程有可能获得此调用的结果,并且当它在if块内执行操作时,其他线程可能会添加或删除元素。

此代码还有一些并发性问题,如果您希望对它们进行讨论,请告诉我们(它们有点偏离主题)。

答案 3 :(得分:0)

正如Germann Arlington指出的那样,queue.isEmpty()的值似乎被缓存在主线程中。尝试同步它:

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

或者只是让队列变得不稳定:

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

答案 4 :(得分:0)

这将解决您的问题。

  

在turnOff()方法的while循环中也使用volatile变量isRunning。

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