0

Below I have constructed an example which synchronizes three threads based on a static variable:

public class CallMethodsInOrder {

    public static void main(String[] args) {
        // Three instances of Thread, first calls first, second second and third third.
        // Ensure that they are all called in order.

        Thread first = new Thread(new FooRunner(new Foo(),MethodToCall.FIRST));
        Thread second = new Thread(new FooRunner(new Foo(),MethodToCall.SECOND));
        Thread third = new Thread(new FooRunner(new Foo(),MethodToCall.THIRD));

        third.start();
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        second.start();
        first.start();
    }
}

class Foo {
    static boolean hasFirstRun = false;
    static boolean hasSecondRun = false;
    static boolean hasThirdRun = false;

    public Foo() {
    }

    public void first() {
        System.out.println("First");
        hasFirstRun = true;
    }

    public void second() {
        System.out.println("Second");
        hasSecondRun = true;
    }

    public void third() {
        System.out.println("Third");
        hasThirdRun = true;
    }
}

class FooRunner implements Runnable{

    private Foo foo;
    private MethodToCall method;

    public FooRunner(Foo foo, MethodToCall method) {
        this.foo = foo;
        this.method = method;
    }

    @Override
    public void run() {
        if(method == MethodToCall.FIRST) {
            foo.first();
        }
        else if (method == MethodToCall.SECOND){
            while(!Foo.hasFirstRun) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            foo.second();
        }
        else if (method == MethodToCall.THIRD) {
            while(!Foo.hasSecondRun) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            foo.third();
        }
    }
}

enum MethodToCall{
    FIRST, SECOND, THIRD;
}

Is this a valid approach? I have read that static variables are not thread safe, however, cannot see a situation in which the above code would not execute the three methods in the desired order (first, second, third).

Many answers I have found have been related to accessing data structures with multiple threads vs ordering of methods as displayed here.

Since each static variable is only being modified by a single thread is it a problem?

  • why don't you try synchronizing? – Scary Wombat Dec 11 '18 at 05:20
  • Check this, https://stackoverflow.com/questions/578904/how-do-synchronized-static-methods-work-in-java – Ketan Dec 11 '18 at 05:21
  • @ScaryWombat and Ketan thanks for the responses. I definitely can use synchronization and that seems like the right answer. I have also looked into using Semaphores instead of a set of shared static variables (when a single instance of Foo is passed to each FooRunner). I think I was just exploring this edge case where you might get away with static variables (none-synchronized) however am sure if multiple threads would be writing to the same variables it would definitely not be recommended. – platinum_pidgeon Dec 11 '18 at 05:41
  • dont forget good old thread.join() - if you need to wait for a thread to finish before proceeding, join with do this without all the fussing about with loops and variables – slipperyseal Dec 11 '18 at 05:57

2 Answers2

1

As suggested in the comments, using synchronized is likely the way forward here. I feel the above code still works with static variables however is definitely not the best practise.

A related solution including Semaphores is included below:

public class CallMethodsInOrder2 {

    public static void main(String[] args) {
        // Three instances of Thread, first calls first, second second and third third.
        // Ensure that they are all called in order.

        // This approach uses Semaphore vs static variables.

        Foo2 foo2 = new Foo2();

        Thread first = new Thread(new FooRunner2(foo2,MethodToCall.FIRST));
        Thread second = new Thread(new FooRunner2(foo2,MethodToCall.SECOND));
        Thread third = new Thread(new FooRunner2(foo2,MethodToCall.THIRD));

        third.start();
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        second.start();
        first.start();
    }

}

class Foo2 {
    private Semaphore one, two;

    public Foo2() {
        one = new Semaphore(1);
        two = new Semaphore(1);

        try {
            one.acquire();
            two.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void first() {
        System.out.println("First");
        one.release();
    }

    public void second() {
        try {
            one.acquire();
            System.out.println("Second");
            one.release();
            two.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void third() {
        try {
            two.acquire();
            two.release();
            System.out.println("Third");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
0

I think the static variable method 'works' in this case (for some value of 'works'), but is definitely less efficient.

You're sleeping an arbitrary amount of time in each thread ('100'), and then waking up to poll this variable. In the case of a semaphore, the OS takes care of the sleep/wake events to the threads.

stellarhopper
  • 660
  • 5
  • 10
  • Thank you for taking the time amongst your time traversing stars. The polling point is a good one and definitely adds to the argument that this shouldn't be an approach taken in real world code. – platinum_pidgeon Dec 11 '18 at 06:05