I am trying to implement a resource handler class.

2019-08-14 22:56发布

问题:

It is a homework assignment. I have two files which were already given. The Client class and the SharedValues Interface. Here is the description:

I need to write a class (resource handler) which contains static interfaces and manage "n" number of resource allocation and its scheduling for "k" number of clients. there must be only two operations for the clients. Reading and writing with no deadlock and no starvation. If one resource is allocated for writing then other clients can't have it for any other purpose. If the resource is allocated for reading then only readers can get it, writers not. A key can make the resources free it got for the allocation. The references of resources are represented by String (names).

The Resource Handler must contain two interfaces for the clients. getLock() and releaseLock(). The required argument of the getLock() interface is an object (Set<String>) in which the names of the resources and the desired operations (boolean: true - writing, false - reading) are placed, the return value is an identifier (long). Until the resource handler can't give the requested resources to the cleint, the resource handler should be blocked the current client on the getLock() call. It will be unblock again when the requested resources are available for the given operation. The return value of the releaseLock() is void, its required argument is an identifier what it got at the getLock() call. The clients are requesting the lock/release of resources as a subsets of resources from the resource handler class (through the getLock() interface) and the clients are releasing the resources by the received identifier. (through the releaseLock() interface).

I am not pro in Java and i have only a little experience in multithreading. Please take into account.

Given the following class and interface:

SharedValues Interface

    public interface SharedValues
    {
        //resources
        public final static String[] RESOURCE_LIST = new String[]{ "a", "b", "c", "d", "e", "f", "g", "h" };

        //constant of the client's writing method type
        public final static boolean WRITE_METHOD = true;

        //constant of the client's reading method type
        public final static boolean READ_METHOD = false;

        //constant for the number of clients
        public final static int CLIENTNUM = 5;

        //minimum wait time of the client
        public final static int CLIENT_HOLD_MINIMUM = 1000;

        //maximum wait time difference of the client
        public final static int CLIENT_HOLD_DIFF = 1000;

        //time limit of the clients
        public final static int RUNTIME = 20000;
}

Client class

import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.ArrayList;

//start and implementation of the client
public class Client extends Thread implements SharedValues
{
    //used random for creating clients
    private static Random mRandom = new Random();

    //for stoping flag
    private boolean mRunning = true;

    //used method in client
    private boolean mMethod = true;

    //the clients want to lock these resources
    private Set<String> mNeededRes = new HashSet<String>();

    //received identifier for the releasing cleint's resources
    private long mLockID = -1;

    //client's logging name
    private String mLogName = null;

    //client's constructor
    public Client( String[] xResList, boolean xMethod, int xClientID )
    {
        super( "Client_" + xClientID );
        mLogName = "Client_" + xClientID;
        mMethod = xMethod;

        for ( int i = 0; i < xResList.length; i++ )
        {
            mNeededRes.add( xResList[ i ] );
        }
    }

    //interface for logging
    private void log( String xMessage )
    {
        System.out.println( new Date() + " " + mLogName + ": " + xMessage );
    }

    //holding resources or sleeping
    private synchronized void holdResources()
    {
        if ( !mRunning )
        {
            return;
        }

        //sleeping a value what is in the intervall
        try
        {
            wait( mRandom.nextInt( CLIENT_HOLD_DIFF ) & CLIENT_HOLD_MINIMUM );
        }
        catch ( InterruptedException e )
        {
            log( "Error: Resource allocating interrupted" );
        }
    }

    //for stopping interface
    public synchronized void stopRunning() throws Exception
    {
        //changing the flag and interrupting the sleep if it has
        if ( mRunning )
        {
            mRunning = false;
            wait();
        }
        else
        {
            log( "Error: the client has already stopped!" );
        }
    }

    //Overloading thread method
    public void run()
    {
        log( "Started." );

        while ( mRunning )
        {
            log( ( ( mMethod == WRITE_METHOD ) ? "Writing" : "Reading" ) + " requested resources: "
                + toSortedSet( mNeededRes ) );

            final long startTime = System.currentTimeMillis();
            mLockID = ResHandler.getLock( mNeededRes, mMethod );
            final long elapsed = System.currentTimeMillis() - startTime;

            log( ( ( mMethod == WRITE_METHOD ) ? "Writing" : "Reading" ) + " received resources (" + elapsed
                + " ms): " + toSortedSet( mNeededRes ) + ". Lock: " + mLockID );

            holdResources();

            ResHandler.releaseLock( mLockID );

            holdResources();
        }

        log( "Stopped." );
    }

    //creating clients
    private static Client createClient( int xClientID )
    {
        final int resNum = mRandom.nextInt( RESOURCE_LIST.length ) + 1;

        //randomly take out one of all resources
        final ArrayList<String> selectedRes = new ArrayList<String>( Arrays.asList( RESOURCE_LIST ) );

        for ( int i = 0; i < ( RESOURCE_LIST.length - resNum ); i++ )
        {
            final int chosenRes = mRandom.nextInt( selectedRes.size() );

            selectedRes.remove( chosenRes );
        }

        final boolean method = mRandom.nextInt( 5 ) <= 2;

        return new Client( ( String[] ) selectedRes.toArray( new String[]{} ), method, xClientID );
    }

    //auxiliary interface for elements of subset, we can do sorted logging
    private String toSortedSet( Set<String> xSet )
    {
        final StringBuffer tmpSB = new StringBuffer( "{ " );

        final String[] sortedRes = ( String[] ) xSet.toArray( new String[]{} );
        Arrays.sort( sortedRes );

        for ( int i = 0; i < sortedRes.length; i++ )
        {
            tmpSB.append( sortedRes[ i ] ).append( ", " );
        }
        tmpSB.setLength( tmpSB.length() - 2 );
        tmpSB.append( " }" );

        return tmpSB.toString();
    }

    public static void main( String[] args ) throws Exception
    {
        //keep the clients for stop
        final Client[] clientArr = new Client[ CLIENTNUM ];

        for ( int i = 0; i < clientArr.length; i++ )
        {
            clientArr[ i ] = createClient( i );
            clientArr[ i ].start();

            //the clients do not start at the same time
            try
            {
                Thread.sleep( mRandom.nextInt( CLIENT_HOLD_MINIMUM ) );
            }
            catch ( InterruptedException e )
            {
                e.printStackTrace();
            }
        }

        //sleeping the running time of clients
        try
        {
            Thread.sleep( RUNTIME );
        }
        catch ( InterruptedException e )
        {
            e.printStackTrace();
        }

        //stopping cleints
        for ( int i = 0; i < clientArr.length; i++ )
        {
            clientArr[ i ].stopRunning();

            try
            {
                clientArr[ i ].join();
            }
            catch ( InterruptedException e )
            {
                e.printStackTrace();
            }
        }
    }
}

I wrote this so far. I see the client log where the Lock is always 0 and the elapsed time 0ms but I do not know why.

Resource Handler class

import java.util.Set;


    class ResHandler {

        private static long identifier;

        public static long getLock(Set<String> mNeededRes, boolean mMethod) {

            return identifier;
        }


        public static void releaseLock(long mLockID) {

        } 

This is the Output:

 Wed Oct 09 04:42:25 CEST 2013 Client_0: Started.

Wed Oct 09 04:42:25 CEST 2013 Client_0: Writing requested resources: { b, c, d, g, h }

Wed Oct 09 04:42:25 CEST 2013 Client_0: Writing received resources (4 ms): { b, c, d, g, h }. Lock: 0

Wed Oct 09 04:42:26 CEST 2013 Client_1: Started.

Wed Oct 09 04:42:26 CEST 2013 Client_1: Writing requested resources: { a, b, c, d, e, f, g, h }

Wed Oct 09 04:42:26 CEST 2013 Client_1: Writing received resources (0 ms): { a, b, c, d, e, f, g, h }. Lock: 0

Wed Oct 09 04:42:26 CEST 2013 Client_0: Writing requested resources: { b, c, d, g, h }

Wed Oct 09 04:42:26 CEST 2013 Client_0: Writing received resources (0 ms): { b, c, d, g, h }. Lock: 0

Wed Oct 09 04:42:26 CEST 2013 Client_1: Writing requested resources: { a, b, c, d, e, f, g, h }

Wed Oct 09 04:42:26 CEST 2013 Client_1: Writing received resources (0 ms): { a, b, c, d, e, f, g, h }. Lock: 0

Wed Oct 09 04:42:26 CEST 2013 Client_2: Started.

Wed Oct 09 04:42:26 CEST 2013 Client_2: Writing requested resources: { a, b, d, e, f, g, h }

Wed Oct 09 04:42:26 CEST 2013 Client_2: Writing received resourcesk (0 ms): { a, b, d, e, f, g, h }. Lock: 0

Wed Oct 09 04:42:27 CEST 2013 Client_0: Writing requested resources: { b, c, d, g, h }

Wed Oct 09 04:42:27 CEST 2013 Client_0: Writing received resources (0 ms): { b, c, d, g, h }. Lock: 0

Wed Oct 09 04:42:27 CEST 2013 Client_3: Started.

Wed Oct 09 04:42:27 CEST 2013 Client_3: Reading requested resources: { a, b, c, d, e, f, g, h }

Wed Oct 09 04:42:27 CEST 2013 Client_3: Reading received resources (0 ms): { a, b, c, d, e, f, g, h }. Lock: 0

Wed Oct 09 04:42:27 CEST 2013 Client_4: Started.

Wed Oct 09 04:42:27 CEST 2013 Client_4: Reading requested resources: { f, h }

Wed Oct 09 04:42:27 CEST 2013 Client_4: Reading received resources (0 ms): { f, h }. Lock: 0

Wed Oct 09 04:42:27 CEST 2013 Client_1: Writing requested resources: { a, b, c, d, e, f, g, h }

Wed Oct 09 04:42:27 CEST 2013 Client_1: Writing received resources (0 ms): { a, b, c, d, e, f, g, h }. Lock: 0

Wed Oct 09 04:42:28 CEST 2013 Client_0: Writing requested resources: { b, c, d, g, h }

Wed Oct 09 04:42:28 CEST 2013 Client_0: Writing received resources (0 ms): { b, c, d, g, h }. Lock: 0

Wed Oct 09 04:42:28 CEST 2013 Client_4: Reading requested resources: { f, h }

Wed Oct 09 04:42:28 CEST 2013 Client_4: Reading received resources (0 ms): { f, h }. Lock: 0

Wed Oct 09 04:42:28 CEST 2013 Client_3: Reading requested resources: { a, b, c, d, e, f, g, h }

Wed Oct 09 04:42:28 CEST 2013 Client_3: Reading received resources (0 ms): { a, b, c, d, e, f, g, h }. Lock: 0

Wed Oct 09 04:42:28 CEST 2013 Client_2: Writing requested resources: { a, b, d, e, f, g, h }

Wed Oct 09 04:42:28 CEST 2013 Client_2: Writing received resources (0 ms): { a, b, d, e, f, g, h }. Lock: 0

Wed Oct 09 04:42:28 CEST 2013 Client_3: Reading requested resources: { a, b, c, d, e, f, g, h }

Wed Oct 09 04:42:28 CEST 2013 Client_3: Reading received resources (0 ms): { a, b, c, d, e, f, g, h }. Lock: 0

Wed Oct 09 04:42:29 CEST 2013 Client_1: Writing requested resources: { a, b, c, d, e, f, g, h }

Wed Oct 09 04:42:29 CEST 2013 Client_1: Writing received resources (0 ms): { a, b, c, d, e, f, g, h }. Lock: 0

Wed Oct 09 04:42:29 CEST 2013 Client_2: Writing requested resources: { a, b, d, e, f, g, h }

Wed Oct 09 04:42:29 CEST 2013 Client_2: Writing received resources (0 ms): { a, b, d, e, f, g, h }. Lock: 0

Wed Oct 09 04:42:29 CEST 2013 Client_4: Reading requested resources: { f, h }

Wed Oct 09 04:42:29 CEST 2013 Client_4: Reading received resources (0 ms): { f, h }. Lock: 0

Wed Oct 09 04:42:29 CEST 2013 Client_0: Writing requested resources: { b, c, d, g, h }

Wed Oct 09 04:42:29 CEST 2013 Client_0: Writing received resources (0 ms): { b, c, d, g, h }. Lock: 0

. . .

I found a half solution on the internet: Resource manager with ReentrantLocks

public class ResHandler {

//ID-s of the granted resource lists
private static long lockNum = 0;

//Resources are identified by strings, each client has a list of demanded resources
//we store these when granted, along with an ID
private static ConcurrentHashMap<Long, Set<String>> usedResources 
    = new ConcurrentHashMap<Long, Set<String>>();

//We store a lock for each resource
private static ConcurrentHashMap<String, ReentrantReadWriteLock> resources 
    = new ConcurrentHashMap<String, ReentrantReadWriteLock>();

//Filling our resources map with the resources and their locks
static {
    for (int i = 0; i < SharedValues.RESOURCE_LIST.length; ++i) {
        String res = SharedValues.RESOURCE_LIST[i];
        //Fair reentrant lock
        ReentrantReadWriteLock lc = new ReentrantReadWriteLock(true);
        resources.put(res, lc);
    }
}

//We get a set of the required resources and the type of lock we have to use
public static long getLock(Set<String> mNeededRes, boolean mMethod) {
    //!!!
    if (mMethod == SharedValues.READ_METHOD) {

        //We try to get the required resources
        for (String mn : mNeededRes)
            resources.get(mn).readLock().lock();

        //After grandted, we put them in the usedResources map
        ++lockNum;
        usedResources.put(lockNum, mNeededRes);
        return lockNum;         
    }

    //Same thing, but with write locks
    else {

        for (String mn : mNeededRes)
            resources.get(mn).writeLock().lock();

        ++lockNum;
        usedResources.put(lockNum, mNeededRes);
        return lockNum;         
    }
}

//Releasing a set of locks by the set's ID
public static void releaseLock(long mLockID) {
    if (!usedResources.containsKey(mLockID)) {
        System.out.println("returned, no such key as: " + mLockID);
        return; 
    }

    Set<String> toBeReleased = usedResources.get(mLockID);

    //Unlocking every lock from this set
    for (String s : toBeReleased) {
        if (resources.get(s).isWriteLockedByCurrentThread())
            resources.get(s).writeLock().unlock();
        else 
            resources.get(s).readLock().unlock();
    }

    //Deleting from the map
    usedResources.remove(mLockID);
}   
}

I tried this and the output was changed to this:

Fri Oct 11 10:14:40 CEST 2013 Client_0: Started.

Fri Oct 11 10:14:40 CEST 2013 Client_0: Reading requested resources: { b, c, h }

Fri Oct 11 10:14:40 CEST 2013 Client_0: Reading received resources (8 ms): { b, c, h }. Lock: 1

Fri Oct 11 10:14:40 CEST 2013 Client_1: Started.

Fri Oct 11 10:14:40 CEST 2013 Client_1: Reading requested resources: { a, b, c, d, f, g, h }

Fri Oct 11 10:14:40 CEST 2013 Client_1: Reading received resources (1 ms): { a, b, c, d, f, g, h }. Lock: 2

Fri Oct 11 10:14:40 CEST 2013 Client_2: Started.

Fri Oct 11 10:14:40 CEST 2013 Client_2: Reading requested resources: { a, b, d, e, f, g, h }

Fri Oct 11 10:14:40 CEST 2013 Client_2: Reading received resources (0 ms): { a, b, d, e, f, g, h }. Lock: 3

Fri Oct 11 10:14:40 CEST 2013 Client_2: Reading requested resources: { a, b, d, e, f, g, h }

Fri Oct 11 10:14:40 CEST 2013 Client_2: Reading received resources (0 ms): { a, b, d, e, f, g, h }. Lock: 4

Fri Oct 11 10:14:41 CEST 2013 Client_3: Started.

Fri Oct 11 10:14:41 CEST 2013 Client_3: Writing requested resources: { h }

Fri Oct 11 10:14:41 CEST 2013 Client_1: Reading requested resources: { a, b, c, d, f, g, h }

Fri Oct 11 10:14:41 CEST 2013 Client_0: Reading requested resources: { b, c, h }

Fri Oct 11 10:14:41 CEST 2013 Client_3: Writing received resources (303 ms): { h }. Lock: 5

Fri Oct 11 10:14:41 CEST 2013 Client_1: Reading received resources (293 ms): { a, b, c, d, f, g, h }. Lock: 6

Fri Oct 11 10:14:41 CEST 2013 Client_0: Reading received resources (171 ms): { b, c, h }. Lock: 7

Fri Oct 11 10:14:41 CEST 2013 Client_3: Writing requested resources: { h }

Fri Oct 11 10:14:41 CEST 2013 Client_4: Started.

Fri Oct 11 10:14:41 CEST 2013 Client_4: Reading requested resources: { a, b, c, d, e, f, g, h }

Fri Oct 11 10:14:42 CEST 2013 Client_3: Writing received resources (633 ms): { h }. Lock: 8

Fri Oct 11 10:14:42 CEST 2013 Client_2: Reading requested resources: { a, b, d, e, f, g, h }

Fri Oct 11 10:14:42 CEST 2013 Client_4: Reading received resources (819 ms): { a, b, c, d, e, f, g, h }. Lock: 9

Fri Oct 11 10:14:42 CEST 2013 Client_2: Reading received resources (163 ms): { a, b, d, e, f, g, h }. Lock: 10

Fri Oct 11 10:14:42 CEST 2013 Client_1: Reading requested resources: { a, b, c, d, f, g, h }

Fri Oct 11 10:14:42 CEST 2013 Client_1: Reading received resourcesk (0 ms): { a, b, c, d, f, g, h }. Lock: 11

Fri Oct 11 10:14:42 CEST 2013 Client_0: Reading requested resources: { b, c, h }

Fri Oct 11 10:14:42 CEST 2013 Client_0: Reading received resources (0 ms): { b, c, h }. Lock: 12

Fri Oct 11 10:14:42 CEST 2013 Client_3: Writing requested resources: { h }

Fri Oct 11 10:14:42 CEST 2013 Client_0: Reading requested resources: { b, c, h }

Fri Oct 11 10:14:43 CEST 2013 Client_1: Reading requested resources: { a, b, c, d, f, g, h }

Fri Oct 11 10:14:43 CEST 2013 Client_3: Writing received resources (447 ms): { h }. Lock: 13

Fri Oct 11 10:14:43 CEST 2013 Client_0: Reading received resources (504 ms): { b, c, h }. Lock: 14

Fri Oct 11 10:14:43 CEST 2013 Client_1: Reading received resources (210 ms): { a, b, c, d, f, g, h }. Lock: 15

Fri Oct 11 10:14:43 CEST 2013 Client_4: Reading requested resources: { a, b, c, d, e, f, g, h }

Fri Oct 11 10:14:43 CEST 2013 Client_4: Reading received resources (0 ms): { a, b, c, d, e, f, g, h }. Lock: 16

Fri Oct 11 10:14:43 CEST 2013 Client_2: Reading requested resources: { a, b, d, e, f, g, h }

Fri Oct 11 10:14:43 CEST 2013 Client_2: Reading received resources (0 ms): { a, b, d, e, f, g, h }. Lock: 17

Fri Oct 11 10:14:43 CEST 2013 Client_1: Reading requested resources: { a, b, c, d, f, g, h }

Fri Oct 11 10:14:43 CEST 2013 Client_1: Reading received resources (0 ms): { a, b, c, d, f, g, h }. Lock: 18

Fri Oct 11 10:14:44 CEST 2013 Client_3: Writing requested resources: { h }

Fri Oct 11 10:14:44 CEST 2013 Client_3: Writing received resources (152 ms): { h }. Lock: 19

Fri Oct 11 10:14:44 CEST 2013 Client_2: Reading requested resources: { a, b, d, e, f, g, h }

Fri Oct 11 10:14:44 CEST 2013 Client_0: Reading requested resources: { b, c, h }

Fri Oct 11 10:14:44 CEST 2013 Client_4: Reading requested resources: { a, b, c, d, e, f, g, h }

Fri Oct 11 10:14:44 CEST 2013 Client_1: Reading requested resources: { a, b, c, d, f, g, h }

Fri Oct 11 10:14:45 CEST 2013 Client_0: Reading received resources (504 ms): { b, c, h }. Lock: 21

Fri Oct 11 10:14:45 CEST 2013 Client_4: Reading received resources (399 ms): { a, b, c, d, e, f, g, h }. Lock: 22

Fri Oct 11 10:14:45 CEST 2013 Client_1: Reading received resources (230 ms): { a, b, c, d, f, g, h }. Lock: 23

Fri Oct 11 10:14:45 CEST 2013 Client_2: Reading received resources (544 ms): { a, b, d, e, f, g, h }. Lock: 20

Fri Oct 11 10:14:45 CEST 2013 Client_1: Reading requested resources: { a, b, c, d, f, g, h }

Fri Oct 11 10:14:45 CEST 2013 Client_1: Reading received resources (0 ms): { a, b, c, d, f, g, h }. Lock: 24

Fri Oct 11 10:14:45 CEST 2013 Client_3: Writing requested resources: { h }

Fri Oct 11 10:14:45 CEST 2013 Client_2: Reading requested resources: { a, b, d, e, f, g, h }

Fri Oct 11 10:14:45 CEST 2013 Client_0: Reading requested resources: { b, c, h }

Fri Oct 11 10:14:46 CEST 2013 Client_4: Reading requested resources: { a, b, c, d, e, f, g, h }

but here the program is frozen. I guess because of deadlock.

My question is: How can I fix this. I would really really apreciate if somebody could show me some working code example.

回答1:

Trying to get the requested locks by looping over the free locks will inevitably result in deadlocks. No client must be granted any lock at all unless the entire set required is available.

One solution:

Use only ONE LOCK that allows one client at a time access to a 'Critical Section' that controls access to the free/allocated sets, the algorithm/s for checking if all the 'locks' required are available and for releasing 'locks'. If a client enters this critical section with a requirement that cannot be satisfied immediately and in its entirety, create an event/semaphore to wait on, store its requirements and event/semaphore in a container, (generate ID here so that the data can be looked up again on release), leave the critical section and wait on the event/semaphore, so blocking it without giving it any locks. When a client enters the critical section to release locks, use the ID to find its data in the container, mark its alocated resouces as free, remove it from the container and then iterate the container looking for any blocked clients that can now obtain ALL their requested locks. If one is found, mark its locks as allocated, leave the critical section and signal the client event/semaphore, so allowing it to run on with ALL its locks allocated.

The trick with complex lock schemes it to not use complex lock schemes :)

You can write the code - it is homework, after all :)

PS - starvation. You can implement anti-starvation in any way you wish. One way would be to 'rotate' the container entries when resources are released and before iterating the container looking for runnable clients. That way, all clients would eventually get a chance to have their required resources looked up first.