简体   繁体   中英

Two threads deadlocking but can't see why, lock released with notifyAll()

using JConsole it seems i get a deadlock situation when 2 threads try to modify this object.

package com.steven.concurrent.assignment2.memoryallocator;
/*
 * This seems to deadlock... cant see why though.
 */
public class MemAllocMonitor implements IMemoryAllocator {

private final int MAX_FREE = 50;
private int freePages = MAX_FREE;

//I think this would work, without even the need for sync blocks.....
// But only in the situaion where i would not have to check the bounds of the updates. If it was just modification, this would be
// fine....
//private volatile int freePages = 50;

public MemAllocMonitor(int pages){
    assert(pages < MAX_FREE);
    this.freePages = pages;
}

public MemAllocMonitor(){

}

@Override
public synchronized void request(int number) {
    if(number < 0)
        throw new IllegalArgumentException();

    while(freePages - number < 0) {
        System.out.println("No space....waiting...");
        try {
            this.wait();                
        } catch (Exception e) {}
    }

        freePages -= number;
        System.out.println("Requested : " + number + " remaining " + freePages);

    this.notifyAll();

}

@Override
public synchronized void release(int number) {
    if(number < 0)
        throw new IllegalArgumentException();

    while(freePages + number > MAX_FREE) {
        System.out.println("page table full....would be " + (number + freePages) );
        try {
            this.wait();                
        } catch (Exception e) {}
    }

    freePages += number;
    System.out.println("Released : " + number + " remaining " + freePages);



    this.notifyAll();

}

@Override
public int getFreePages() {
    return freePages;
}

}

This object is accessed via a simple wrapper that implements runnable, and calls either method as shown below.

package com.steven.concurrent.assignment2.memoryallocator;

import concurrent.RandomGenerator;
import concurrent.Time;

public class MemAllocRequester implements Runnable, MemoryAllocatorAction{

private IMemoryAllocator memoryAllocator;
private volatile boolean shutdown = false;;

public MemAllocRequester(IMemoryAllocator memAlloc){
    this.memoryAllocator = memAlloc;

}


@Override
public void run() {
    while(!shutdown){
        Time.delay(500);
        memoryAllocator.request(RandomGenerator.integer(0, 30));
    }

}

public void ShutDown(){
    this.shutdown = true;
}

}

and

package com.steven.concurrent.assignment2.memoryallocator;

import concurrent.RandomGenerator;
import concurrent.Time;

public class MemAllocReleaser implements Runnable, MemoryAllocatorAction{

private IMemoryAllocator memoryAllocator;
private volatile boolean shutdown = false;;

public MemAllocReleaser(IMemoryAllocator memAlloc){
    this.memoryAllocator = memAlloc;

}


@Override
public void run() {
    while(!shutdown){
        Time.delay(500);
        memoryAllocator.release(RandomGenerator.integer(0, 30));
    }

}

public void ShutDown(){
    this.shutdown  = true;
}

}

It is started off as such...

package com.steven.concurrent.assignment2.memoryallocator;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MemAllocMain {


public static void main(String[] args){

    ExecutorService executor = Executors.newFixedThreadPool(10);


    //IMemoryAllocator memoryAllocator = new MemAllocSemaphore();
    IMemoryAllocator memoryAllocator = new MemAllocMonitor();


    System.out.println("Starting app with " + memoryAllocator.getFreePages() + " pages...");

    Thread t1 = new Thread(new MemAllocRequester(memoryAllocator));
    Thread t2 = new Thread(new MemAllocReleaser(memoryAllocator));

    t1.setName("MEMORY REQUESTER £££££££££££££££££££");
    t2.setName("MEMORY RELEASER £££££££££££££££££££");

    executor.submit(t1);
    executor.submit(t2);


}

}

I have implemented a solution using the semaphore class, but for some reason this is causing trouble using the default java monitor solution. It runs for about 30 seconds, then both threads go into their waiting state, even though the lock should be enforced.

The problem is that both threads are hitting the upper and lower bounds (50 and 0 respectively) at the same time. Both examples below highlight the deadlock.

Scenario 1

  1. request(29) - freePages=21
  2. request(30) - under 0 so waits
  3. release(30) - over 50 so waits : deadlock

Scenario 2

  1. request(29) - freePages=21
  2. release(30) - over 50 so waits
  3. request(30) - under 0 so waits : deadlock

I am not sure what the exact requirements are for the homework problem but you need to revisit the release and request methods. I see two viable solutions:

  1. Change the release method so that it only releases up to MAX_FREE but will still return
  2. Change the release method so that it can release a subset of the amount requested, notifyAll, reenter the wait so it can release the remaining amount.

Also, you are kind of using the ExecutionService wrong. The ExecutionService is what creates the Threads so there is no reason for you to create the threads like you are doing.

Thread t1 = new Thread(new MemAllocRequester(memoryAllocator));
Thread t2 = new Thread(new MemAllocReleaser(memoryAllocator));

The threads you are creating will actually never be 'started' as Threads. It is still working for you because the ExecutionService threads will call your Thread.run() which will call MemAlloc*.run(). ie your t1 and t2 threads just pass the run() call along and provide no value.

Your MemAllocRequester and MemAllocReleaser are Runnables so just pass those into the ExecutionService directly.

executor.submit(new MemAllocRequester(memoryAllocator));
executor.submit(new MemAllocReleaser(memoryAllocator));

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