Можно ли когда-нибудь использовать статические переменные для синхронизации потоков? - PullRequest
0 голосов
/ 11 декабря 2018

Ниже я построил пример, который синхронизирует три потока на основе статической переменной:

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

Это правильный подход?Я читал, что статические переменные не являются потокобезопасными, однако не могу увидеть ситуацию, в которой приведенный выше код не будет выполнять три метода в нужном порядке (первый, второй, третий).

У меня много ответовОбнаружено, что они связаны с доступом к структурам данных с несколькими потоками в сравнении с порядком методов, как показано здесь.

Поскольку каждая статическая переменная изменяется только одним потоком, это проблема?

Ответы [ 2 ]

0 голосов
/ 11 декабря 2018

Я думаю, что метод статических переменных в этом случае «работает» (для некоторого значения «работает»), но определенно менее эффективен.

Вы спите произвольное количество времени в каждом потоке ('100'), а затем просыпается, чтобы опросить эту переменную.В случае семафора ОС заботится о событиях сна / пробуждения для потоков.

0 голосов
/ 11 декабря 2018

Как предлагается в комментариях, использование синхронизированных, вероятно, путь вперед.Я чувствую, что приведенный выше код по-прежнему работает со статическими переменными, однако это определенно не лучшая практика.

Соответствующее решение, включая семафоры, включено ниже:

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