Search code examples
javamultithreadingcountdownlatch

Start Threads same time and dispute global variables


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();
                
            }
    
        }

Solution

  • 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)