I have a multithreaded program where one thread is reading data and multiple others are doing work on that data. If I have one writing thread continuously adding data (Example.add()) and the other reader threads sequentially reading that data (Example.getData(1), Example.getData(2), ...), what is the best way to block the readers until data at the index they are requesting is available?
This problem is kind of like producer-consumer, but I don't want to "consume" the data.
public class Example {
private ArrayList<Integer> data;
public Example() {
data = new ArrayList<Integer>();
}
public int getData(int i) {
// I want to block here until the element
// index i is available.
return data.get(i);
}
public void add(int n) {
data.add(n);
}
}
This seems to be a reasonable way to synchronize threads:
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantLock.html
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html
The Condition link shows an example of this:
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
Please don't judge me on the code style this is a straight copy from the example in Condition.
In your case you might consider using a single lock which all threads wait on, that signals when new elements are added. this would cause all threads to wake up and test if their element is there yet. if not they go back to wait for the next signal.
If you want them to specifically wait for the 1 element you could keep a signal per element but that seems overkill.
something like:
public class Example {
private Lock lock = new ReentrantLock();
private Condition update = lock.newCondition();
public Example(data) {
data = new ArrayList<Integer>();
}
public int getData(int i) {
lock.lock();
try {
while (data.get(i) == null) {
update.await();
}
return data.get(i);
} finally {
lock.unlock();
}
}
public void add(int n) {
data.add(n);
update.signal();
}
}
You can use blocking queue in java. When the queue is empty it blocks for the queue to have data until it is consumed. You can find more information about it here : https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html
Lookup some examples online for Java blocking queue and you can solve your issue
Related
I am supposed to be using two custom Semaphore classes (binary and counting) to print off letters in an exact sequence. Here is the standard semaphore.
public class Semaphore {
protected int value;
public Semaphore() {
value = 0;
}
public Semaphore(int initial) {
value = (initial >=0) ? initial : 0;
}
public synchronized void P() throws InterruptedException {
while (value==0) {
wait();
}
value--;
}
public synchronized void V() {
value++;
notify();
}
}
And here is the binary semaphore:
public class BinarySemaphore extends Semaphore {
public BinarySemaphore(boolean unlocked) {super(unlocked ? 1 : 0);}
public synchronized void P() throws InterruptedException{
while(value==0) {
wait();
}
value=0;
}
public synchronized void V() {
value=1;
notify();
}
}
Here is the main bulk of the code, except for a reason I can't work out why the threads stop after around thirty or so repetitions. Wait isn't called, the criteria for being true are being reached, so why aren't they working? Any help is much appreciated.
BinarySemaphore binaryWXSemaphore = new BinarySemaphore(false);
BinarySemaphore binaryYZSemaphore = new BinarySemaphore(false);
Semaphore countingWSemaphore = new Semaphore();
Semaphore countingYZSemaphore = new Semaphore();
Runnable runnableW = () -> {
while(true) {
if (binaryWXSemaphore.value == 0 && countingYZSemaphore.value >= countingWSemaphore.value) {
binaryWXSemaphore.V();
countingWSemaphore.V();
System.out.println("W");
}
}
};
Runnable runnableX = () -> {
while(true) {
if (binaryWXSemaphore.value == 1) {
try {
binaryWXSemaphore.P();
System.out.println("X");
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
Runnable runnableY = () -> {
while(true) {
if (binaryYZSemaphore.value == 0 && countingWSemaphore.value > countingYZSemaphore.value) {
binaryYZSemaphore.V();
countingYZSemaphore.V();
System.out.println("y");
}
}
};
Runnable runnableZ = () -> {
while(true) {
if (binaryYZSemaphore.value == 1 && countingWSemaphore.value > countingYZSemaphore.value) {
try {
binaryYZSemaphore.P();
countingYZSemaphore.V();
System.out.println("z");
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
As #iggy points out the issue is related to fact that different threads are reading different values of value, because the way you access it isn't thread safe. Some threads may be using an old copy of the value. Making it volatile will mean each thread access reads more consistent value:
protected volatile int value;
Or switch to AtomicInteger which ensures thread consistent change to the int stored in value. You'll also need to replace the assignments using set/get/inc/decrement methods of AtomicInteger:
protected final AtomicInteger value = new AtomicInteger();
// Then use value.set(0 / 1)
// or value.incrementAndGet / decrementAndGet
Unfortunately, even with the above changes, you may find other issues because value could change in the duration between each Runnable's if statement, and the operations inside those if branches.
Also: replacing notify() by notifyAll() usually gives better multi-thread handling though I don't think this necessarily helps in your example.
I have two thread that can produce value and add it in a arraylist,
and other thread can access to it to read a value.
My problem is that the producer can access to the list in the same time that the consumer use data.
This is my code :
public class CommandTree
{
Lock lock = new ReentrantLock();
ArrayList<Command> cmdToSend = null;
JSONObject sendCmdMap;
public CommandTree(JSONObject sendCmdMap)
{
this.cmdToSend = new ArrayList<Command>();
this.sendCmdMap = sendCmdMap;
}
private synchronized void addMacroCmd(String macro, int fmt, int tgt, int sid,int count,JSONArray sli,String paramName,JSONObject params,int function)
{
boolean check = false;
int i = 0;
lock.lock();
try
{
for(i=0; i<cmdToSend.size(); i++)
{
if(cmdToSend.get(i).getMacroName().equalsIgnoreCase(macro))
{
check = true;
break;
}
}
if(check == false)
{
cmdToSend.add(new Command(macro,fmt,tgt,sid,count,function,sli));
}
if(paramName != null)
{
if(check)
cmdToSend.get(i).setParameter(paramName,params);
else
cmdToSend.get(cmdToSend.size()-1).setParameter(paramName,params);
}
}
finally
{
lock.unlock();
}
}
private void addParameter(String macro,int fmt, int tgt, int sid,int count,JSONArray sli,String paramName,JSONObject params,int function)
{
lock.lock();
try
{
this.addMacroCmd(macro, fmt, tgt, sid, count,sli, paramName,params,function);
}
finally
{
lock.unlock();
}
}
public int getSize()
{
return cmdToSend.size();
}
public void reset()
{
lock.lock();
try
{
cmdToSend.clear();
}
finally
{
lock.unlock();
}
}
/*
public Command getNextCommandInLoop()
{
return cmdToSend.;
}
*/
public Command getNextCommand(int i)
{
Command result;
lock.lock();
try
{
result = cmdToSend.get(i);
}
finally
{
lock.unlock();
}
return result;
}
public synchronized boolean populateCommandTree(String i,String target) throws JSONException
{
JSONObject tgtCmd = (JSONObject) sendCmdMap.get(target);
JSONObject cmdObject;
Iterator<String> iter = tgtCmd.keys();
while (iter.hasNext())
{
String key = iter.next();
if(key.equalsIgnoreCase(i))
{
//it is a general commands
JSONObject macro = (JSONObject)tgtCmd.opt(key);
cmdObject = (JSONObject) macro.opt("cmd");
addMacroCmd(key,cmdObject.optInt("fmt"),cmdObject.optInt("tgt"),cmdObject.optInt("sid"),cmdObject.optInt("count"),cmdObject.optJSONArray("sli"),null,null,macro.optInt("function"));
return true;
}
else
{
//It is a parameter, we have to search its general command
cmdObject = (JSONObject)tgtCmd.opt(key);
if(cmdObject == null)
{
continue;
}
JSONObject parameter = cmdObject.optJSONObject("Parameter");
if( parameter == null)
{
//There isn't the requested command, we iterate on the next one
continue;
}
else
{
if(((JSONObject) parameter).optJSONObject(i) != null)
{
JSONObject cmdStructure = (JSONObject)cmdObject.opt("cmd");
//We have found the command, save it in commandSendCache
addMacroCmd(key,cmdStructure.optInt("fmt"),cmdStructure.optInt("tgt"),cmdStructure.optInt("sid"),cmdStructure.optInt("count"),cmdStructure.optJSONArray("sli"),i,parameter.optJSONObject(i),cmdObject.optInt("function"));
return true;//(JSONObject)tgtCmd.opt(key);
}
else
{
continue;
}
}
}
}
return false;
}}
I read some post on that case, but I don't understand very well. I thought to post my code in this way I can understand in better way.
Other problem is that one producer is a UI thread, and I worried if there is problem to stop the UI thread for some times.
I also thought to use ConcurrentLinkedQueue because some time I need to loop on the list, and I always extract the value from the first position, but with concurrentLInkedQueue I don't know how can implementate the loop and in what way I can implementate the addMacroCmd method..
In my case I think to use lock object and ArrayList.
Do you have some suggestion ? I want to learn in better way the concurrency, but it not very easy for me :(
EDIT : the following is the part of code that add and remove the command :
public synchronized void readSensorData(String[] sensor, String target)
{
cmdTree.reset();
for(int i=0;i<sensor.length;i++)
{
try
{
cmdTree.populateCommandTree(sensor[i],target);
}
catch (JSONException e)
{
}
}
writeExecutor.execute(this.writeCommandTree);
}
/**
*
* #param i
* #param target
* #return
* #throws JSONException when the command requested doesn't exists
*/
private ByteArrayOutputStream f = new ByteArrayOutputStream();
ExecutorService writeExecutor = Executors.newSingleThreadExecutor();
Semaphore mutex = new Semaphore(0);
volatile boolean diagnostic = false;
volatile int index = 0;
Runnable writeCommandTree = new Runnable()
{
#Override
public void run()
{
while(index < cmdTree.getSize())
{
writeCmd();
try
{
mutex.acquire();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
sendAnswerBroadcast("answer", answer);
answer = new JSONObject();
index = 0;
}
};
and the mutex is release when arrive a new response .
Addictional information :
The readSensorData() is called when button on the ux (UI Thread) is
pressed and in same case from other Thread B. WriteCommandTree is only
execute in the executor (Other Thread C).
I change the name of getnextcommand into getcommand
- getcommand(int i) is called in the callback of the response (sometime is in other thread (i'm forget to that function ...) and in writecmd inside writecommandtree
- getsize in the writecommandTree in the thread C
Don't get headaches just for synchronizing a list, simply use the Java standard library :
List<Command> commands = Collections.synchronizedList(new ArrayList<>());
By the way, a naive implementation of this would simply to wrap an unsafe list and add synchronized to all the methods.
You can use blockingQueue to achieve the same. Refer simple tutorial about blockingQueue :http://tutorials.jenkov.com/java-util-concurrent/blockingqueue.html
There are several problems with this code:
It is unlikely that you need both a ReentrantLock and synchronization.
The getSize method is not synchronized at all. If, e.g., reset is called from a thread other than the one from which getSize is called, the program is incorrect.
sendCmdMap is leaked in CommandTree's constructor. If the thread that creates the CommandTree is different from the thread that calls populateCommandTree, the program is incorrect.
Note, btw, that using a synchronized view of cmdToSend would not fix any of these problems.
What you need to do, here, is this:
Producers need to seize a lock, hand a command to the CommandTree and then delete all references to it.
Consumers need to seize the same lock and get a reference to a command, deleting it from the CommandTree.
For problems like this, there is no better reference than "Java Concurrency in Practice"
I am trying to make my class as Queue such that if I have two threads, one that adds and other that removes elements - they could do that at the same time. Currently one gets blocked because the threads are competing for the same lock object.
public class Queue {
private Cell head, tail;
public synchronized void add(Object o){
Cell c = new Cell(o);
if (tail == null) { head = c; }
else { tail.next = c; }
c.next = null;
tail = c;
notifyAll();
}
public synchronized Object remove()
throws InterruptedException {
while (head == null){
wait();
}
Cell c = head;
head = head.next;
if (head == null){ tail = null; };
return c.contents;
}
}
class Cell {
Cell next;
Object contents;
public Cell(Object o) { contents = o; }
}
Main:
public static void main(String[] args) throws InterruptedException {
Queue q = new Queue();
Thread thr = new Thread(new Runnable() {
#Override
public void run() {
for (int i = 0; i < 1000; i++)
q.add(new Integer(i));
}
});
Thread thr1 = new Thread(new Runnable() {
#Override
public void run() {
for (int i = 0; i < 10000; i++)
try {
q.remove();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
thr.start();
thr1.start();
}
Standard synchronisation techniques will not achieve what you require. That is, concurrent updates to the queue. This is because when one thread acquires a lock on the queue, another thread then cannot acquire a lock on the queue and so cannot proceed.
The technique that you have to implement in order to achieve concurrent updates to the queue is called lock stripping. This is how concurrent collections such as ConcurrentHashMap achieve concurrent reads and writes. Implementing lock stripping is not trivial.
You need to ask yourself whether if implementing a custom collection with lock stripping will be easier than if you choose a JDK collection such as ConcurrentLinkedDeque.
I need a semaphore with the following features:
it should be non-blocking, i.e. if the thread cannot get the permit
it should go further without waiting
it should be nonreentrant, i.e. if the same thread enters the
guarded piece of code twice it should take away two permits instead of
one
I have written the following code:
public class SimpleSemaphore
{
private int permits;
private AtomicLong counter = new AtomicLong();
SimpleSemaphore(int permits)
{
this.permits = permits;
}
boolean acquire()
{
if (counter.incrementAndGet() < permits)
{
return true;
}
else
{
counter.decrementAndGet();
return false;
}
}
void release()
{
counter.decrementAndGet();
}
}
Another option is this Semaphore:
public class EasySemaphore
{
private int permits;
private AtomicLong counter = new AtomicLong();
EasySemaphore(int permits)
{
this.permits = permits;
}
boolean acquire()
{
long index = counter.get();
if (index < permits)
{
if (counter.compareAndSet(index, index + 1))
{
return true;
}
}
return false;
}
void release()
{
counter.decrementAndGet();
}
}
Are the both implementations thread-safe and correct?
Which one is better?
How would you go about this task?
Doesn't java.util.concurrent.Semaphore already do all that?
It has a tryAcquire for non-blocking acquire, and it maintains a simple count of remaining permits (of which the same thread could take out more than one).
I would say the second one is better as the counter will never be greater thathan 0 (and its slightly more efficient)
I would use a loop otherwise you can have the method fail when there is still permits left.
public class EasySemaphore {
private final AtomicInteger counter;
EasySemaphore(int permits) {
counter = new AtomicInteger(permits);
}
boolean acquire() {
// highly unlikely to loop more than once.
while(true) {
int count = counter.get();
if (count <= 0) return false;
if (counter.compareAndSet(count, count -1))
return true;
}
}
void release() {
counter.incrementAndGet();
}
}
I have a need for a single-permit semaphore object in my Java program where there is an additional acquire method which looks like this:
boolean tryAcquire(int id)
and behaves as follows: if the id has not been encountered before, then remember it and then just do whatever java.util.concurrent.Semaphore does. If the id has been encountered before and that encounter resulted in the lease of the permit then give this thread priority over all other threads who may be waiting for the permit. I'll also want an extra release method like:
void release(int id)
which does whatever the java.util.concurrent.Semaphore does, plus also "forgets" about the id.
I don't really know how to approach this, but here's the start of a possible implementation but I fear it's going nowhere:
public final class SemaphoreWithMemory {
private final Semaphore semaphore = new Semaphore(1, true);
private final Set<Integer> favoured = new ConcurrentSkipListSet<Integer>();
public boolean tryAcquire() {
return semaphore.tryAcquire();
}
public synchronized boolean tryAcquire(int id) {
if (!favoured.contains(id)) {
boolean gotIt = tryAcquire();
if (gotIt) {
favoured.add(id);
return true;
}
else {
return false;
}
}
else {
// what do I do here???
}
}
public void release() {
semaphore.release();
}
public synchronized void release(int id) {
favoured.remove(id);
semaphore.release();
}
}
EDIT:
Did some experiment. Please see this answer for results.
In principle, Semaphore has a queue of threads internally, so like Andrew says if you make this queue a priority queue and poll from this queue to give out permits, it probably behaves the way you want. Note that you can't do this with tryAcquire because that way threads don't queue up. From what I see looks like you'd have to hack the AbstractQueuedSynchronizer class to do this.
I could also think of a probabilistic approach, like this:
(I'm not saying that the code below would be a good idea! Just brainstorming here. )
public class SemaphoreWithMemory {
private final Semaphore semaphore = new Semaphore(1);
private final Set<Integer> favoured = new ConcurrentSkipListSet<Integer>();
private final ThreadLocal<Random> rng = //some good rng
public boolean tryAcquire() {
for(int i=0; i<8; i++){
Thread.yield();
// Tend to waste more time than tryAcquire(int id)
// would waste.
if(rng.get().nextDouble() < 0.3){
return semaphore.tryAcquire();
}
}
return semaphore.tryAcquire();
}
public boolean tryAcquire(int id) {
if (!favoured.contains(id)) {
boolean gotIt = semaphore.tryAcquire();
if (gotIt) {
favoured.add(id);
return true;
} else {
return false;
}
} else {
return tryAquire();
}
}
Or have the "favoured" threads hang out a little bit longer like this:
EDIT: Turns out this was a very bad idea (with both fair and non-fair semaphore) (see my experiment for details.
public boolean tryAcquire(int id) {
if (!favoured.contains(id)) {
boolean gotIt = semaphore.tryAcquire(5,TimeUnit.MILLISECONDS);
if (gotIt) {
favoured.add(id);
return true;
} else {
return false;
}
} else {
return tryAquire();
}
I guess this way you can bias the way permits are issued, while it won't be fair. Though with this code you'd probably be wasting a lot of time performance wise...
For blocking acquisition model, what about this:
public class SemWithPreferred {
int max;
int avail;
int preferredThreads;
public SemWithPreferred(int max, int avail) {
this.max = max;
this.avail = avail;
}
synchronized public void get(int id) throws InterruptedException {
boolean thisThreadIsPreferred = idHasBeenServedSuccessfullyBefore(id);
if (thisThreadIsPreferred) {
preferredThreads++;
}
while (! (avail > 0 && (preferredThreads == 0 || thisThreadIsPreferred))) {
wait();
}
System.out.println(String.format("granted, id = %d, preferredThreads = %d", id, preferredThreads));
avail -= 1;
if (thisThreadIsPreferred) {
preferredThreads--;
notifyAll(); // removal of preferred thread could affect other threads' wait predicate
}
}
synchronized public void put() {
if (avail < max) {
avail += 1;
notifyAll();
}
}
boolean idHasBeenServedSuccessfullyBefore(int id) {
// stubbed out, this just treats any id that is a
// multiple of 5 as having been served successfully before
return id % 5 == 0;
}
}
Assuming that you want the threads to wait, I hacked a solution that is not perfect, but should do.
The idea is to have two semaphores and a "favourite is waiting" flag.
Every thread that tries to acquire the SemaphoreWithMemory first tries to acquire the "favouredSemaphore". A "favoured" thread keeps the Semaphore and a non-favoured releases it immediately. Thereby the favoured thread blocks all other incoming threads once he has acquired this Semaphore.
Then the second "normalSemaphore" has to be acquired to finish up.
But the non-favoured thread then checks again that there is no favoured thread waiting using a volatile variable). If none is waiting then he simply continues; if one is waiting, he releases the normalSemaphore and recursively calls acquire again.
I am not really sure that there are no race conditions lurking. If you want to be sure, you perhaps should refactor your code to hand of "work items" to a priority queue, where another thread takes the work item with the highest priority and executes that code.
public final class SemaphoreWithMemory {
private volatile boolean favouredAquired = false;
private final Semaphore favouredSemaphore = new Semaphore(1, true);
private final Semaphore normalSemaphore = new Semaphore(1, true);
private final Set<Integer> favoured = new ConcurrentSkipListSet<Integer>();
public void acquire() throws InterruptedException {
normalSemaphore.acquire();
}
public void acquire(int id) throws InterruptedException {
boolean idIsFavoured = favoured.contains(id);
favouredSemaphore.acquire();
if (!idIsFavoured) {
favouredSemaphore.release();
} else {
favouredAquired = true;
}
normalSemaphore.acquire();
// check again that there is no favoured thread waiting
if (!idIsFavoured) {
if (favouredAquired) {
normalSemaphore.release();
acquire(); // starving probability!
} else {
favoured.add(id);
}
}
}
public void release() {
normalSemaphore.release();
if (favouredAquired) {
favouredAquired = false;
favouredSemaphore.release();
}
}
public void release(int id) {
favoured.remove(id);
release();
}
}
I read this article by Ceki and was interested how biased semaphore acquisition could be (since I felt the "biased locking" behavior would make sense in semaphores as well..). On my hardware with 2 processors and a Sun JVM 1.6, it actually results in pretty uniform lease.
Anyways, I also tried to "bias" the leasing of semaphore with the strategy I wrote in my other answer. Turns out a simple extra yield statement alone results in significant bias. Your problem is more complicated, but perhaps you can do similar tests with your idea and see what you get :)
NOTE The code below is based upon Ceki's code here
Code:
import java.util.concurrent.*;
public class BiasedSemaphore implements Runnable {
static ThreadLocal<Boolean> favored = new ThreadLocal<Boolean>(){
private boolean gaveOut = false;
public synchronized Boolean initialValue(){
if(!gaveOut){
System.out.println("Favored " + Thread.currentThread().getName());
gaveOut = true;
return true;
}
return false;
}
};
static int THREAD_COUNT = Runtime.getRuntime().availableProcessors();
static Semaphore SEM = new Semaphore(1);
static Runnable[] RUNNABLE_ARRAY = new Runnable[THREAD_COUNT];
static Thread[] THREAD_ARRAY = new Thread[THREAD_COUNT];
private int counter = 0;
public static void main(String args[]) throws InterruptedException {
printEnvironmentInfo();
execute();
printResults();
}
public static void printEnvironmentInfo() {
System.out.println("java.runtime.version = "
+ System.getProperty("java.runtime.version"));
System.out.println("java.vendor = "
+ System.getProperty("java.vendor"));
System.out.println("java.version = "
+ System.getProperty("java.version"));
System.out.println("os.name = "
+ System.getProperty("os.name"));
System.out.println("os.version = "
+ System.getProperty("os.version"));
}
public static void execute() throws InterruptedException {
for (int i = 0; i < THREAD_COUNT; i++) {
RUNNABLE_ARRAY[i] = new BiasedSemaphore();
THREAD_ARRAY[i] = new Thread(RUNNABLE_ARRAY[i]);
System.out.println("Runnable at "+i + " operated with "+THREAD_ARRAY[i]);
}
for (Thread t : THREAD_ARRAY) {
t.start();
}
// let the threads run for a while
Thread.sleep(10000);
for (int i = 0; i< THREAD_COUNT; i++) {
THREAD_ARRAY[i].interrupt();
}
for (Thread t : THREAD_ARRAY) {
t.join();
}
}
public static void printResults() {
System.out.println("Ran with " + THREAD_COUNT + " threads");
for (int i = 0; i < RUNNABLE_ARRAY.length; i++) {
System.out.println("runnable[" + i + "]: " + RUNNABLE_ARRAY[i]);
}
}
public void run() {
while (!Thread.currentThread().isInterrupted()) {
if (favored.get()) {
stuff();
} else {
Thread.yield();
// try {
// Thread.sleep(1);
// } catch (InterruptedException e) {
// Thread.currentThread().interrupt();
// }
stuff();
}
}
}
private void stuff() {
if (SEM.tryAcquire()) {
//favored.set(true);
counter++;
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
SEM.release();
} else {
//favored.set(false);
}
}
public String toString() {
return "counter=" + counter;
}
}
Results:
java.runtime.version = 1.6.0_21-b07
java.vendor = Sun Microsystems Inc.
java.version = 1.6.0_21
os.name = Windows Vista
os.version = 6.0
Runnable at 0 operated with Thread[Thread-0,5,main]
Runnable at 1 operated with Thread[Thread-1,5,main]
Favored Thread-0
Ran with 2 threads
runnable[0]: counter=503
runnable[1]: counter=425
Tried with 30 seconds instead of 10:
java.runtime.version = 1.6.0_21-b07
java.vendor = Sun Microsystems Inc.
java.version = 1.6.0_21
os.name = Windows Vista
os.version = 6.0
Runnable at 0 operated with Thread[Thread-0,5,main]
Runnable at 1 operated with Thread[Thread-1,5,main]
Favored Thread-1
Ran with 2 threads
runnable[0]: counter=1274
runnable[1]: counter=1496
P.S.: Looks like "hanging out" was a very bad idea. When I tried calling SEM.tryAcquire(1,TimeUnit.MILLISECONDS); for favored threads and SEM.tryAcquire() for non-favored threads, non-favored threads got the permit almost 5 times more than the favored thread!
Also, I'd like to add that these results are only measured under 1 particular situation, so it's not clear how these measures behave in other situations.
It strikes me that the simplest way to do this is not to try and combine Semaphores, but to build it from scratch on top of monitors. This is generally risky, but in this case, as there are no good building blocks in java.util.concurrent, it's the clearest way to do it.
Here's what i came up with:
public class SemaphoreWithMemory {
private final Set<Integer> favouredIDs = new HashSet<Integer>();
private final Object favouredLock = new Object();
private final Object ordinaryLock = new Object();
private boolean available = true;
private int favouredWaiting = 0;
/**
Acquires the permit. Blocks until the permit is acquired.
*/
public void acquire(int id) throws InterruptedException {
Object lock;
boolean favoured = false;
synchronized (this) {
// fast exit for uncontended lock
if (available) {
doAcquire(favoured, id);
return;
}
favoured = favouredIDs.contains(id);
if (favoured) {
lock = favouredLock;
++favouredWaiting;
}
else {
lock = ordinaryLock;
}
}
while (true) {
synchronized (this) {
if (available) {
doAcquire(favoured, id);
return;
}
}
synchronized (lock) {
lock.wait();
}
}
}
private void doAcquire(boolean favoured, int id) {
available = false;
if (favoured) --favouredWaiting;
else favouredIDs.add(id);
}
/**
Releases the permit.
*/
public synchronized void release() {
available = true;
Object lock = (favouredWaiting > 0) ? favouredLock : ordinaryLock;
synchronized (lock) {
lock.notify();
}
}
}