2

In my program I have one thread incrementing a supply variable by 2, then another thread takes a random number of supply from a supply class. The supply class can only store up to 5 values and because the sleep and supply requests are random, the supply count can increment over it's max limit.

What I'm trying to make sure is that it doesn't go over that limit.

Is there a better way to do this?

(pseudocode)

  1. increment supply by 2
  2. if supply is more than max then assign supply to max

Here is the code:

private int MAX = 5;
private int supply = 0;
public void run()
{
    while(true) {
      supply = supply + 2;
      if(supply > MAX) 
          supply = MAX;
    }
}
rgettman
  • 176,041
  • 30
  • 275
  • 357
user2273278
  • 1,275
  • 2
  • 14
  • 19
  • 3
    I only see one thread here. The code that you have shown will almost instantaneously increment `supply` above the max and stay there. – Robert Harvey Apr 30 '13 at 23:10
  • I would really recommend looking into this http://stackoverflow.com/questions/4818699/practical-uses-for-atomicinteger – Sergey Benner Apr 30 '13 at 23:27
  • The title does not really match the description, what you are looking for is a semaphore. – flup May 01 '13 at 12:03

6 Answers6

4

You could use a public synchronized incSupply() method which is used to increment the supply variable:

public synchronized void incSupply()
{
    // Code borrowed from Jean-Bernard Pellerin.
    int temp = supply + 2;
    if (temp > MAX)
        temp = MAX;
    supply = temp;
}

Note that you need to use synchronized also for other methods that read/write from/to the 'supply' variable.

Jonny Dee
  • 837
  • 4
  • 12
1
int temp = supply + 2;
if (temp > MAX)
  temp = MAX;
supply = temp;

This is still not thread-safe though. You should look into locks and synchronization.

Jean-Bernard Pellerin
  • 12,556
  • 10
  • 57
  • 79
1

If you have more than one thread you should declare your common resource,(meaning other threads execute commands on that variable) in your case i guess would be the supply, as synchronized. and use fine-grained synchronization

synchronized(this) {
     supply = supply+2
 }
Stelios Savva
  • 812
  • 1
  • 10
  • 30
  • Your suggestion lacks checking the condition that 'supply' must not exceed a maximum value. Also, for real fine-graind synchronization you would not use 'sychronized(this)' but 'synchronized(supplyMutex)', where 'supplyMutex' might be just an 'Object' instance solely used for synchronization purpose. – Jonny Dee May 01 '13 at 06:39
1

I dont entirely understand this part of your question: and the other thread takes a random number of supply from the supply class. Does this mean the consuming thread can take a random number of values from the supply class, or a value which is a random number?

In any case, you have a resource which requires mutually exclusive access, so you need to ensure that only one thread can modify it. In java, this is easily done by requesting a intrinsic lock on the instance being modified, only one thread can hold this lock at any given time. Now, when a thread encounters a synchronized block (as per Stelsavva's example) it automatically will try and obtain the intrinsic lock on whichever instance this represents, and hold it until the end of the block. If any other thread encounters the block, while another thread is holding the lock, it will wait until the lock is released by the other thread.

Hence, the execution is of the block is synchronised and you wont have problems with interleaving.

JustDanyul
  • 13,813
  • 7
  • 53
  • 71
1

Use a Semaphore with five permits. Somewhat counterintuitively, I think the permit would represent the permission to store a supply, so the first thread would need to acquire permits to store the supplies. When the second thread takes supplies, it releases this many permits.

flup
  • 26,937
  • 7
  • 52
  • 74
0

The simplest solution is to use AtomicInteger without going throughout the trouble of synchronizing the increment:

private int MAX = 5;
private AtomicInteger supply = new AtomicInteger(0);
public void run()
{
    while(true) {
      if(supply.addAndGet(2) > MAX) 
          supply.set(MAX);
    }
}
Stephan
  • 8,000
  • 3
  • 36
  • 42
  • You are suggesting to implement a [non-blocking algorithm](http://en.wikipedia.org/wiki/Non-blocking_algorithm). Such algorithms can be very efficient, but they are also more difficult to write. I'd go for a 'synchronized' version as long as high real-time efficiency is not as important, because most of the time such code is much simpler to maintain. – Jonny Dee Apr 30 '13 at 23:59
  • maybe i wasn't clear, i didn't said you should implement it just use the one that already is... pls read the docs from my answer : http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/atomic/AtomicInteger.html – Stephan May 01 '13 at 00:40
  • I had read the docs, why have you been thinking I did not? And your suggested 'incrementAndGet()' method would not only use 'AtomicInteger#addAndGet(int)' method, but also 'AtomicInteger#compareAndSet(int, int)', because the supply value must not exceed a MAX value. And because "another thread takes a random number of supply" the other thread would also have to use corresponding primitives. So you actually do write a non-blocking algorithm -- as I said. – Jonny Dee May 01 '13 at 06:33
  • I think I was too fast in selecting the right combination of operation primitives for 'incrementAndGet' method (which supports my argument getting a non-blocking algorithm right is more difficult). Maybe you replace your pseudo-code with a working example? – Jonny Dee May 01 '13 at 06:50
  • 1
    Your code is not thread safe: there is a window where `supply` might be seen as greater than `MAX` by another reading thread... – assylias May 01 '13 at 20:27
  • 1
    Your answer answers the title of the question, but the question below it is trickier than the title suggests. – flup May 02 '13 at 07:15