2

I need to build what I call an "Unfair Semaphore" with priority. For example : When a thread with priority = 1 wants to acquire the semaphore, it just has to wait until the other thread with the same priority finished, then it can acquire(). But when a thread with priority = 2 wants to acquire the semaphore, it has to wait for all threads with priority = 1 to finish before using the semaphore, and then try to acquire(). I have a total of 4 different priorities. Here is what I tried but it didn't work.

Does someone have any solution ?

public class UnfairSemaphore
{
    private Semaphore mPrior1;
    private Semaphore mPrior2;
    private Semaphore mPrior3;
    private Semaphore mPrior4;

    public UnfairSemaphore()
    {
        mPrior1 = new Semaphore(1);
        mPrior2 = new Semaphore(1);
        mPrior3 = new Semaphore(1);
        mPrior4 = new Semaphore(1);
    }

    public void acquire(int priority) throws InterruptedException
    {
        if(priority == 1)
        {
            mPrior1.acquire();
        }
        else if(priority == 2)
        {
            while(mPrior1.hasQueuedThreads() && mPrior1.availablePermits() <=0)
            {
                //wait();
            }
            mPrior2.acquire();
            mPrior1.acquire();
        }
        else if(priority == 3)
        {
            while(mPrior1.hasQueuedThreads() && mPrior1.availablePermits() <=0 && mPrior2.hasQueuedThreads() && mPrior2.availablePermits() <=0)
            {
                //wait();
            }
            mPrior3.acquire();
            mPrior2.acquire();
            mPrior1.acquire();
        }
        else
        {
            while(mPrior1.hasQueuedThreads() && mPrior1.availablePermits() <=0 && mPrior2.hasQueuedThreads() && mPrior2.availablePermits() <=0 && mPrior3.hasQueuedThreads() && mPrior3.availablePermits() <=0)
            {
                //wait();
            }
            mPrior4.acquire();
            mPrior3.acquire();
            mPrior2.acquire();
            mPrior1.acquire();
        }
    }

    public void release(int priority)
    {
        if(priority == 1)
        {
            mPrior1.release();
        }
        else if(priority == 2)
        {           
            mPrior1.release();
            mPrior2.release();          
        }
        else if(priority == 3)
        {           
            mPrior1.release();
            mPrior2.release();
            mPrior3.release();
        }
        else
        {           
            mPrior1.release();
            mPrior2.release();
            mPrior3.release();
            mPrior4.release();
        }
        //notifyAll();
    }
}
CerebralFart
  • 3,336
  • 5
  • 26
  • 29
Vanpourix
  • 185
  • 7

2 Answers2

0

The basic requirement is that, we have a bunch of waiting threads, each with a priority. On release(), we select a waiting thread with the highest priority, and wake it up.

This can be done by thread park() and unpark() in LockSupport. It is a low-level concurrency tool, but we don't use it for eliteness; we use it because it most naturally models our solution.

First we need a data structure that stores waiting threads along with their priorities. You can do this in many ways.

    void storeWaitingThread(Integer priority, Thread thread) { ... }

    // select and remove a waiting thread with highest priority; return null if none.
    Thread selectWaitingThread(){ ... }

Now, release() will just select a waiting thread and unpark it

final Object lock = new Object();

volatile Thread owningThread;

public void release()
{
    synchronized (lock)
    {
        Thread nextOwner = selectWaitingThread();
        owningThread = nextOwner;
    }
    LockSupport.unpark(owningThread);
}

acquire(priority) will store the current thread as a waiting thread; park until it is selected

public void acquire(int priority)
{
    Thread thisThread = Thread.currentThread();
    synchronized (lock)
    {
        if(owningThread==null)
        {
            owningThread=thisThread;
            return;
        }
        storeWaitingThread(priority, thisThread);
    }

    while(owningThread!=thisThread)
    {
        LockSupport.park();
        // interrupt ignored. // todo handle interrupt
    }
}

See full code at https://gist.github.com/zhong-j-yu/3ea91194f55d91059789

Note that our acquire will ignore interrupts. If it should bail on interrupts, add some logic after park() waking up.

ZhongYu
  • 19,446
  • 5
  • 33
  • 61
-1

Why managing the threading by yourself ?

You should use the synchronised keyword and the setPriority method from the Thread class.

Look at the following links :

https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#setPriority%28int%29

https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

Hello_world
  • 303
  • 2
  • 11
  • Probably good advice, but "priorities" in the standard edition of Java have very little meaning. If the OP _needs_ the priorities to really mean something (i.e., if the application has hard real-time requirements), then it should be run on a real-time Java platform. – Solomon Slow Feb 09 '16 at 16:47
  • Thread priorities don't do what you seem to think they're doing. Hell synchronized isn't a fair lock to begin with (good choice, fair locks are horrible wrt performance) – Voo Feb 09 '16 at 20:27
  • The problem is the priority does'nt depend on the Thead but on "where the thread need to access the ressource". Then sometimes it will be with the less priority, sometimes with the higher. – Vanpourix Feb 10 '16 at 09:49