Reputation: 3628
I have two methods, request()
and release()
, each thread that access request()
must take control until it call release()
, it means that no other thread will enter the request method until the current thread hasn't finished.
I need to implement this behavior with both the java monitor and the semaphores.
This is the class of the thread:
public class Process implements Runnable
{
private Thread thread;
private final int ID;
private Resource g;
private final int MIN = 1000, MAX = 5000;
private void delay()
{
try
{
Thread.sleep( (int) Math.random() * (MAX - MIN) + MIN);
} catch( InterruptedException ex){}
}
public Process(int ID, Resource g)
{
this.thread = new Thread(this, "P#" + ID);
this.ID = ID;
this.g = g;
}
public void run()
{
while(true)
{
g.request(ID);
delay();
g.release();
delay();
}
}// run
public void start()
{
this.thread.start();
}
}
Using the Semaphores it's easy to implement this behavior, I take the mutex in the request()
method and release it in the release()
method:
public class ResourceS implements Resource
{
private Semaphore mutex = new Semaphore(1);
public void request(int ID)
{
try
{
mutex.acquire();
} catch (InterruptedException e){}
System.out.println(Thread.currentThread().getName() + " enter");
}
public void release()
{
System.out.println(Thread.currentThread().getName() + " exit");
mutex.release();
}
}
In a main I launch five thread, here is the output:
P#0 enter
P#0 exit
P#1 enter
P#1 exit
P#2 enter
P#2 exit
P#3 enter
P#3 exit
P#4 enter
P#4 exit
P#0 enter
P#0 exit
P#1 enter
P#1 exit
P#2 enter
P#2 exit
P#3 enter
As you can see it is correct, because each thread enter only when another has exited. Using the monitor there is a problem, here is the code:
public class ResourceMonitor implements Resource
{
private Object lock = new Object();
private boolean oneInside = false;
public void request(int ID)
{
synchronized (lock)
{
while(oneInside)
{
try { lock.wait(); } catch(InterruptedException e){}
}
System.out.println(Thread.currentThread().getName() + " enter");
oneInside = true;
}
}
public void release()
{
synchronized(lock)
{
if (oneInside)
{
lock.notifyAll();
oneInside = false;
}
System.out.println(Thread.currentThread().getName() + " exit");
}
}
}
This is the output using the monitor.
P#0 enter
P#0 exit
P#4 enter
P#4 exit
P#0 enter
P#0 exit
P#4 enter
P#4 exit
P#0 enter
P#0 exit
P#4 enter
P#4 exit
Only two threads enter and exit, sometimes instead of P#4
is P#1
. Do you know what is happening, and how to get the same ouptut I'm having using the semaphores?
Here is the code of the main for those who want to test it:
public static void main(String args[])
{
Resource g = new ResourceS();
Process p0 = new Process(0, g);
Process p1 = new Process(1, g);
Process p2 = new Process(2, g);
Process p3 = new Process(3, g);
Process p4 = new Process(4, g);
p0.start();
p1.start();
p2.start();
p3.start();
p4.start();
}
Upvotes: 2
Views: 859
Reputation: 1172
Here's what's happening:
P1 requests, locks while executing request(). As soon as your synchronized section ends (i.e. the function ends), you release the lock.
P1 delays()
While P1 delays, another process can execute the request() method.
Now, in the milliseconds it takes for P1's request code to execute, it's likely anything in that interim would have hit the lock that P1 had, and is now waiting.
In conclusion, your delay() must be within the synchronized section of code or you need to use a monitor at a higher level (or just use reentrant locks/semaphores)
Extra: You might also find AtomicBoolean helpful.
Upvotes: 1
Reputation: 27115
A synchronized(lock) {...}
block acquires the mutex associated with lock
upon entering the block, and it releases the mutex when it leaves the block.
You can't use synchronized
to do exactly what you asked, because there's no way to separate acquiring the lock from releasing it.
You could move the synchronization up to a higher level in the code (i.e., always do this):
public void run() {
while(true) {
synchronized(lock) {
g.request(ID);
delay();
g.release();
}
delay();
}
}
But if it's a hard requirement to leave the synchronization where it is, then you'll either have to go on using semaphores (which is not a bad idea), or use something similar.
You could use java.util.concurrent.locks.ReentrantLock
instead of using Semaphore
. It's a slightly lighter-weight solution to your problem, but maybe not 'lighter enough' to make a difference.
Why do you want to change out the semaphore for something else?
Upvotes: 0