Search code examples
javamultithreadingnotify

Java release lock after notify causes new threads to appear


![enter image description here][1]does anyone have an idea regarding to why new threads are created when releasing lock after notifyall? I have one thread listening for request status (STRING) to change (waiting on lock object) and the other one changing it:

@Override
public void run() {
    while(!customerGroupDetails.isEmpty()){
        RentalRequest request=customerGroupDetails.remove();
        management.addRentalRequest(request);
        getLogger().info(Thread.currentThread().getName() + ": handling new request:" + request.toString());
        getLogger().info(Thread.currentThread().getName() + ": waiting for request status to be FULFILLED");
        request.waitForStatus("FULFILLED");
        ExecutorService ex=Executors.newCachedThreadPool();
        CompletionService<Double> cs= new ExecutorCompletionService<Double>(ex);
        CreateStaySimulationForClient stay=new CreateStaySimulationForClient(cs,request.getDuration());
        request.setRequestStatus("INPROGRESS");
        getLogger().info(Thread.currentThread().getName() + ": Simulating stay and setting requets status to INPROGRESS");
        customerGroupDetails.forEach(stay);
        Future<Double> damage=null;
        Double recivedDamage=new Double(0);
        double sum=0;
        int numberOfCustomers=customerGroupDetails.size();
        for(int i=0;i<numberOfCustomers;i++){
            try {
                damage=cs.take();
                recivedDamage=damage.get();
                getLogger().info(" damage for assetContent is " + recivedDamage);
                sum=sum+recivedDamage;



            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ExecutionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        getLogger().info(Thread.currentThread().getName() + ": Asset is damaged by " + sum);
        request.getAsset().damage(sum);
        if(request.getAsset().getHealth()<65){
            management.changeAssetStatus(request.getAsset(), "UNAVILABLE");
            DamageReport damageReport=new DamageReport(request.getAsset(), sum);
            management.addDamageReport(damageReport);
        }
        else{
            management.changeAssetStatus(request.getAsset(), "AVAILABLE");
            getLogger().info(request.getAsset() + " is now available");
        }

        request.setRequestStatus("COMPLETE");
        getLogger().info(Thread.currentThread().getName() + ": damage report has been submitted to managment - this request status is COMPLETE");

    }
    getLogger().info(Thread.currentThread().getName() + ": no more requests for me - i'm done");

}

@Override
public void run() {
    int totalTime = 0;
    boolean shiftEnded = false;
    getLogger().info(clerkDetails.toString() + ": starting new shift");
    while (!shiftEnded&& !Thread.currentThread().isInterrupted()) {
        RentalRequest req = null;
        getLogger().info(clerkDetails.toString() + ": trying to take request");
        try {
            req = rentalRequests.take();
        } catch (InterruptedException e) {
            if (numberOfRequests.get() == 0) {
                getLogger()
                        .info(clerkDetails.toString()
                                + " was interrupted becuase there are no more requests");
                shiftEnded=true;
            } else {
                getLogger().warning(
                        clerkDetails.toString()
                                + " was interrupted unexpectedly"
                                + e.toString());

            }
        }
        if (req!=null) {
            int num=numberOfRequests.decrementAndGet();
            getLogger().info(clerkDetails.toString() + ": recived request: "+req.toString()+",number of requests left is " +num +", now finding appropreiate asset");
            Asset asset = assets.getAsset(req.getAssetType(),
                    req.getAssetSize());
            assets.changeAssetStatus(asset, "BOOKED");

            getLogger().info(clerkDetails.toString() + ": Asset " + asset.toString() + " is now BOOKED");
            req.setAsset(asset);

            management.updateEarnings(asset.getCostPerNight()
                    * req.getDuration());

            int time = getClerkDetails().getLocation().calculateDistance(
                    asset.getLocation());
            totalTime = totalTime + time;
            getLogger().info(clerkDetails.toString() + ": going to Asset - sleeping:" + time);

            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                getLogger().warning(
                        clerkDetails.toString()
                                + " was interrupted unexpectedly"
                                + e.toString());



            }

            req.setRequestStatus("FULFILLED");

            getLogger().info(clerkDetails.toString() + ": request status set to: FULFILLED");
            getLogger().info(clerkDetails.toString()  +": time left for this shift is " + (8 -totalTime));

            if (totalTime > 8) {
                shiftEnded = true;


            }
            if (numberOfRequests.get() == 0) {
                shiftEnded=true;
                management.interrupt();

                getLogger().info(clerkDetails.toString() + ": finished last requets for this simulation - tyring to interrupet managment");
            }

        }


    }
    management.countDownClerksBarriar();
    getLogger().info(clerkDetails.toString() + ": finished shift");

}

public void setRequestStatus(String status) {
    synchronized(statusLock){
        //if(!getRequestStatus().equalsIgnoreCase(status)){
            requestStatus = status;
            statusLock.notifyAll();
        //}
    }


}
public void waitForStatus(String status){
    synchronized (statusLock) {
    while(!getRequestStatus().equalsIgnoreCase(status)){

            try {
                statusLock.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }
}

everything is working as expected except new threads are created after the release of the lock, using visualvm profiler i got the thread dump and they all show :

"pool-15-thread-2" prio=6 tid=0x000000000c20e000 nid=0x23d8 waiting on condition [0x00000000112be000]
   java.lang.Thread.State: TIMED_WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000000e0badf08> (a java.util.concurrent.SynchronousQueue$TransferStack)
    at java.util.concurrent.locks.LockSupport.parkNanos(Unknown Source)
    at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(Unknown Source)
    at java.util.concurrent.SynchronousQueue$TransferStack.transfer(Unknown Source)
    at java.util.concurrent.SynchronousQueue.poll(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.getTask(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

   Locked ownable synchronizers:
    - None

needless to say - all the threads i create have name so i know its not one of them, all the threads i created are done after 30-40 sec all the threads die and the process finally stop test done to locate the cause:

  1. tried reproducing the issue on smaller code that has message board - one thread waiting for the string to change and the other one changing it - didnt cause the issue
  2. doing notifyall on the same object doesnt reproduce the issue if there is no thread waiting on it
  3. tried using other objects as locks - problem presists

for info: using jdk1.7.0_71 on windows 7 x64 running on eclipse

any help will be appreciated


Solution

  • This line: ExecutorService ex=Executors.newCachedThreadPool(); creates a thread pool. This line: CreateStaySimulationForClient stay=new CreateStaySimulationForClient(cs,request.getDuration()); presumably, creates something, using that thread pool.

    The thread you mentioned in your snipped is one of the threads from that pool. It does have a name, which has "pool" in it, so that you know where it came from :)

    Also, note that you are doing this (thread pool creation) in a loop, which is almost certainly not what you want to do.