-2

I am new to concurrent programming and I am facing few issues with the below code using Java threads.

Status Class (this class tracks the position availability):

public class Status {

    private static Map<String, Boolean> positions = new HashMap<>();

    static {
        //Initially all positions are free (so set to true)
        positions.put("A", true);
        positions.put("B", true);
    }

    public synchronized void occupyOrClear(String position, 
                                    boolean status) {
         positions.put(position, status);
   }

    public boolean isClear(String position) {
        return positions.get(position);
    }
}

MyThread Class:

public class MyThread implements Runnable {

    private String[] positions;

    private String customer;

    public MyThread(String customer, String[] positions) {
        this.positions = positions;
        this.customer = customer;
    }

    private Status status = new Status();

    public void run() {
        for (int i = 0; i < positions.length;) {
            String position = positions[i];
            if (status.isClear(position)) {
                // position occupied now
                status.occupyOrClear(position, false);
                System.out.println(position + " occupied by :"+customer);

                try {
                    //my real application logic goes below (instead of sleep)
                    Thread.sleep(2000);
                } catch (InterruptedException inteExe) {
                    System.out.println(" Thread interrupted ");
                }

                // Now clear the position
                status.occupyOrClear(position, true);
                System.out.println(position + " finished & cleared by:"+customer);
                i++;
            } else {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException inteExe) {
                    System.out.println(" Thread interrupted ");
                }
            }
        }
    }
}

ThreadTest Class:

public class ThreadTest {
    public static void main(String[] args) {
    String[] positions = { "A", "B"};
    Status status = new Status();
    Thread customerThread1 = new Thread(new MyThread(status, "customer1", positions));
    Thread customerThread2 = new Thread(new MyThread(status, "customer2", positions));
    Thread customerThread3 = new Thread(new MyThread(status, "customer3", positions));
    customerThread1.start();
    customerThread2.start();
    customerThread3.start();
}
}

Even though I have used 'synchronized' I could notice that some times Thread3 is picking up prior to Thread2 and could you please help me to resolve this issue and to acheive the following results ?

(1) Always customerThread1 should take the positions first and then followed by customerThread2 and then customerThread3 (etc...)

(2) As soon as the A's position is freed by customerThread1, the position should be immediately picked up by customerThread2 (rather than customerThread2 and customerThread3 waiting till all positions are done by customerThread1).And as soon as customerThread2 finishes position 'A', then customerThread3 should pick it up, etc..

(3) As soon as the position (A, B, etc..) is freed/available, the next customerThread should pick it up immediately.

(4) The solution should avoid all race conditions

Vasu
  • 21,832
  • 11
  • 51
  • 67
  • 4
    I see no attempt whatsoever at concurrent programming here, except starting several threads. How about reading some introductory material on the subject before asking strangers to help you out? – Marko Topolnik Sep 27 '16 at 12:59
  • 1
    You are not using the same `status` reference in your threads. Each thread is creating a new Status object. – David SN Sep 27 '16 at 13:12
  • Dadiv: Status has got static initializer, so it is not a problem whether we pass it or not. However, I have changed it now and passed it to all threads. – Vasu Sep 27 '16 at 13:22
  • Marko: I have already tried 'synchronization' of the methods, but don't want to go for it as it causes huge performance issues. – Vasu Sep 27 '16 at 13:23
  • Why does it cause huge performance issues? – John Vint Sep 27 '16 at 13:40
  • John: I wanted to mention that if there is any other better alternative solution without using 'synchronized' – Vasu Sep 27 '16 at 13:43
  • The point of my question, is that if you don't know why synchronized is causing you performance problems, then you really can't tackle the problem. Like David SN mentioned, you are using a new `status` object. This will mean the JVM will probably elide the synchronization away. – John Vint Sep 27 '16 at 13:49
  • If you want to do three (or any other number of) things in some particular order, then they right way to do it is to do them all in a _single thread._ Any time you say, "I want to do X, Y, and Z concurrently," you are saying that you don't care about the order in which they are done. – Solomon Slow Sep 27 '16 at 22:37

2 Answers2

1

There are several fundamental problems.

  1. You have broken code and already noticed that it doesn’t work. But instead of asking how to fix that broken code, you are asking for alternatives with higher performance. You will never manage to write working programs with that attitude.

  2. Apparently, you have no idea, what synchronized does. It acquires a lock on a particular object instance which can be held by one thread only. Therefore, all code fragments synchronizing on the same object are enforced to be executed ordered, with the necessary memory visibility. So your code fails for two reasons:

    1. You are creating multiple instances of Status accessing the same objects referenced by a static variable. Since all threads use different locks, this access is entirely unsafe.
    2. Your occupyOrClear is declared synchronized, but your method isClear is not. So even if all threads were using the same lock instance for occupyOrClear, the result of isClear remained unpredictable due to its unsafe access to the map.
  3. You have code of the form
    if(status.isClear(position)) { status.occupyOrClear(position, false); …
    which matches the check-then-act anti-pattern. Even if each of these two method calls were thread-safe, this sequence still remained unsafe, because between these two invocations, everything can happen, most notably, the condition, the thread just checked, may change without the thread noticing. So two or more threads could invoke isClear, receiving true and then proceed with occupyOrClear.

  4. You are using Thread.sleep.

Holger
  • 285,553
  • 42
  • 434
  • 765
0

You can try with the following pseudocode:

main() {
    //some concurrent queues, eg ConcurrentLinkedQueue
    Queue t1Tasks = new Queue("A","B","C");
    Queue t2Tasks = new Queue();
    Queue t3Tasks = new Queue();
    Thread t1 = new PThread(t1Tasks,t2Tasks,"customer1");
    Thread t2 = new PThread(t2Tasks,t3Tasks,"customer2");
    Thread t3 = new PThread(t3Tasks,null,"customer3");
}

PThread {
       Queue q1,q2;
       PThread(Queue q1, Queue q2,...){}
       run() {
            while (item = q1.get()) {
                  //process item
                  q2.put(item); //to be processed by next thread
            }
       }
}
olsli
  • 831
  • 6
  • 8