【问题标题】:Java thread enters while condition even if the condition is falseJava线程即使条件为假也进入while条件
【发布时间】:2020-03-29 16:39:13
【问题描述】:

我目前正在尝试编写一个将客户发送到最短队列的应用程序。每个队列都有自己的线程。 在 Queue 类中,我有一个实例变量 nrClients,用于跟踪队列中有多少客户端。

在 run 方法中,我有一个 while 条件,仅当 nrClients 大于 0 时才保持线程运行,但有时即使 nrClients 为 0,它也会进入该 while 循环,从而在代码中触发 NullPointerException在那段时间里面。我还有一个从0nrClients-1 的for 循环,但有时它会进入该循环并给我一个Index 0 out of bounds for length 0 异常。这是我在该课程中编写的代码:

public class Queue implements Runnable{
    BlockingQueue<Client> queue;
    private int nrClients;
    private AtomicInteger waitTime;
    private boolean isClosed=true;

    public Queue(){
        nrClients= 0;
        waitTime = new AtomicInteger(0);
        queue = new LinkedBlockingQueue<Client>();
    }

    public void addClient(Client c){
        nrClients++;
        queue.add(c);
        waitTime.addAndGet(c.getServiceTime());
        c.setWaitTime(waitTime.get());
    }

    private void removeClient(Client c){
        nrClients--;
        queue.remove();
    }

    @Override
    public void run() {
            while (nrClients>0) {
                isClosed = false;
                Client c = queue.peek();
                try {
                    int mustWait = c.getServiceTime();
                    for (int i = 0; i < mustWait; i++) {
                        Thread.sleep(10);
                        c.decServiceTime();
                        waitTime.decrementAndGet();
                    }

                } catch (InterruptedException e) {
                }
                removeClient(c);
            }
            if (nrClients == 0)
                isClosed = true;
    }

    public AtomicInteger getWaitTime() {
        return waitTime;
    }

    public String toString(){
        if(nrClients==0){
            return "closed";
        }
        else
        {
            String r="";
            for(int i=0; i<nrClients; i++){
                Client c = (Client) queue.toArray()[i];
                r+="("+c.getID()+","+c.getArrivalTime()+","+c.getServiceTime()+"); ";
            }
            return r;
        }
    }

    public boolean isClosed() {
        return isClosed;
    }

我似乎找不到导致这些异常的问题,如果您能指出问题所在,我将不胜感激。提前谢谢!

编辑:我试过让 nrClients volatile / AtomicInteger,都没有解决问题。此外,通过用 queue.size() 替换 nrClients,我仍然会在 toString 方法中包含的 for 循环中获得索引异常。

【问题讨论】:

  • 我已经尝试过 queue.size() 但它仍然在那个 for 循环中遇到异常,索引 0 超出了长度 0 的范围
  • 请注意增量运算符不是线程安全的。

标签: java multithreading


【解决方案1】:

如果其他一些线程调用addClient,则nrClient 会增加对象c 添加到queue 之前。如果 while 循环恰好在此时运行,nrClient 为 1,但 queue 仍为空。

您需要 synchronise 方法 addClient 访问 nrClientqueue (可以通过实现方法 getNrClient() 然后实现两个方法 getNrClientaddClientsynchronized(lock) 和一个共同的Object lock = new Object())。

更好:避免一起使用 nrClient。而是检查 queue.peek 是否为 null 或使用 poll。

for 循环中 queue.size() 的问题类似,你在不同的时间调用 queue.size() 和 queue.toArray(),因此它们可能不一致。而是:在 for 循环之外获取数组并使用 array.length。

【讨论】:

  • queue.size() 会比nrClient 更好吗?我更喜欢使用它而不是 queue.peek() 因为我在 toString 方法内的 for 循环中也需要它。问题是,在for(int i=0; i&lt;queue.size(); i++) 循环中,它仍然给我一个 Index 0 out of bounds for length 0 异常。
  • 没有。在 for 循环之外获取数组并使用 array.length。 (我更新了我的答案)。
【解决方案2】:

看起来好像您正在另一个线程中执行run。由于 nrClients 不是 volatile 它不需要读取任何新值。话虽如此,我不喜欢其余代码的外观。

【讨论】:

  • 是的,我正在另一个线程中执行这个线程。我试过让 nrClients volatile / AtomicInteger 但都没有解决问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-05
  • 2017-07-21
  • 2021-02-07
  • 2014-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多