简体   繁体   中英

Java thread enters while condition even if the condition is false

I currently try to write an application that sends customers to the shortest queue. Each queue has its own thread. In the Queue class I have an instance variable nrClients that keeps track of how many clients there are in the queue.

In the run method I have a while condition that keeps the thread running only if nrClients is larger than 0, but sometimes it enters that while loop even if nrClients is 0, triggering a NullPointerException in the code inside that while. I also have a for loop that goes from 0 to nrClients-1 , but sometimes it enters that loop and gives me an Index 0 out of bounds for length 0 exception. Here's the code I have written in that class:

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

I can't seem to find the problem that leads to these exceptions, I would appreciate if you could point out what's wrong. Thank you in advance!

Edit: I've tried making nrClients volatile / AtomicInteger, neither fixed the problem. Also, by substituting nrClients with queue.size() I am still getting the index exception in the for loop included in the toString method.

If some other thread calls addClient , the nrClient is increased before the object c is added to the queue . If the while loop runs exactly at this time, nrClient is 1 but the queue is still empty.

You need to synchronise the method addClient with access to nrClient and queue (one way this can be done is by implementing a method getNrClient() and then both methods getNrClient and addClient are synchronized(lock) with a common Object lock = new Object() ).

Better: avoid using nrClient all together. Instead check for queue.peek not being null or use poll.

The problem with queue.size() in the for loop is similar, you call queue.size() and queue.toArray() at different times, hence they may be inconsistent. Instead: Fetch the array outside the for loop and use array.length.

It looks as if you are executing run in a different thread. As nrClients is not volatile it needn't be reading any new values. Having said that, I don't like the look of the rest of the code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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