0

I'm trying to understand how to implement Threads disputing global variables. In my implementation I created 2 variables and I want 4 Threds (e.g.) to dispute it by decrementing.

The first problem is that the way I implemented to consume will always follow an order (first Thread decrements the flake ice cream and the second Thread decrements the chocolate ice cream).

Is there any way to improve this rule?

And I wouldn't want to know what would be the best place to use CountDownLatch

public class IceCream implements Runnable {

    private static int flake = 1;
    private static int chocolate = 1;
    @Override
    public void run() {
        buyIceCream();
        
    }   
    
    private void synchronized buyIceCream() {
try {
            
            if(IceCream.flake>0) {
                System.out.println("");
                System.out.println("successfully purchased " +Thread.currentThread().getName());
                Thread.sleep(200);
                IceCream.flake--;
                System.out.println("");             
            }
            else if(IceCream.chocolate>0) {
                System.out.println("");
                System.out.println("successfully purchased " +Thread.currentThread().getName());
                Thread.sleep(200);
                IceCream.chocolate--;
                System.out.println(""); 
            }
            else {
                System.out.println("No more ice cream " +Thread.currentThread().getName());
            }
        }
            catch(Exception e) {
                
            }       
        
    }
}
public static void main(String[] args) throws InterruptedException {
            IceCream c = new IceCream();
            Thread[] t = new Thread[4]; 
            for(int i = 0; i<t.length; i++) {
                t[i] = new Thread(c);
                t[i].setName("Kid"+ i);
                t[i].start();
                t[i].join();
                
            }
    
        }
billy_ta
  • 3
  • 1
  • Your question is not clear. What is the overall purpose of your experiment? What exactly does "improve this rule" mean? Regarding "best place to use CountDownLatch", what is the latch for, and by what criteria do we judge "best" place? – Basil Bourque Mar 18 '22 at 20:32
  • Improve the way a Thread "buys" flake ice cream and buys chocolate ice cream. And the correct word was not the best place, but the correct one. Because I wanted the Threads to start all at the same time. And I don't know if I use CountDownLatch in main or in my IceCream class – billy_ta Mar 18 '22 at 20:40
  • 1
    [`Executors.invokeAll`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ExecutorService.html#invokeAll(java.util.Collection)) will submit a bunch of tasks at about the same time, but not precisely simultaneous. You cannot control precisely when threads execute, not using conventional programming (Java or otherwise) on conventional hardware with conventional operating systems. See [real-time computing](https://en.wikipedia.org/wiki/Real-time_computing) & [real time Java](https://en.wikipedia.org/wiki/Real_time_Java). – Basil Bourque Mar 18 '22 at 20:47

1 Answers1

0

This is a mistake:

t[i].start();
t[i].join();

The join() call does not return until the thread has ended, so your program will never allow more than one thread to run at the same time. You can fix the mistake by writing two separate loops. The first one creates and starts all of the threads, and then the second one joins all of them.

for(int i = 0; i<t.length; i++) {
    t[i] = new Thread(c);
    t[i].setName("Kid"+ i);
    t[i].start();
}
for(int i = 0; i<t.length; i++) {
    t[i].join();    
}

This is another mistake:

public void run() {
    buyIceCream();
}   
    
private void synchronized buyIceCream() {
    ...
}

Wrapping everything that a thread does inside a single synchronized method is another way to ensure that no two of your threads will be allowed to run at the same time.

At the very least, you should remove the sleep() calls from the synchronized block:

private void buyIceCream() {
    String message = "No more ice cream ";
    synchronized (this) {
        if(IceCream.flake>0) {
            IceCream.flake--;
            message = "Ice cream flake purchased by" + Thread.currentThread().getName();
        }
        else if(IceCream.chocolate>0) {
            IceCream.chocolate--;
            message = "Chocolate ice cream purchased by" + Thread.currentThread().getName();
        }
    }
    System.out.println(message);
    try {
        Thread.sleep(200);
    }
    catch(Exception e) {
        System.out.println("Never, EVER, completely ignore an exception.");
    }       
}

I also moved the System.out.println() call out of the synchronized block because I know that the characters printed by any single call will not be interleaved with characters printed by a single call from a different thread. (See https://stackoverflow.com/a/9459743/801894)

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57
  • In the sysout logic you made, can I capture a sysout for each ice cream? Example: `System. out.println("Ice cream flake purchased by" + Thread.currentThread().getName());` and `System. out.println("Chocolate ice cream purchased by" + Thread.currentThread().getName());` – billy_ta Mar 18 '22 at 21:54
  • @billy_ta, See my updated (simplified!) answer, above. – Solomon Slow Mar 19 '22 at 17:17