Простая синхронизация в Java - PullRequest
0 голосов
/ 16 мая 2018

Я изучал документацию Oracle по синхронизации, и там был счетчик классов:

class Counter {
    int c = 0;

    public void increment() {
        c++;
    }

    public void decrement() {
        c--;
    }

    public int value() {
        return c;
    }
}

И они немного объясняют некоторые возможные неправильные чередования с двумя потоками, я пытался написать программу для тестированияэти неправильные чередования:

public class TestPara {

    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        AdderCounter ac = new AdderCounter(counter);
        SubberCounter sc = new SubberCounter(counter);

        Thread t1 = new Thread(ac);
        Thread t2 = new Thread(sc);

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(counter.c);
    }   
}

class AdderCounter implements Runnable{
    Counter counter;

    public AdderCounter(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        counter.increment();
        System.out.println(counter.value());
    }
}

class SubberCounter implements Runnable{
    Counter counter;

    public SubberCounter(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        counter.decrement();
        System.out.println(counter.value());
    }
 }

Но независимо от того, синхронизирован ли доступ к c или нет, я всегда получаю значения 0, 0 и 0, и я не знаю почему.

Кто-нибудь может мне помочь?С наилучшими пожеланиями.

Ответы [ 3 ]

0 голосов
/ 16 мая 2018

Вам нужно запустить add / sub много раз параллельно.

private static final int COUNT = 1000000;

class Counter {
    int c = 0;

    public void increment() {
        c++;
    }

    public void decrement() {
        c--;
    }

    public int value() {
        return c;
    }
}

class AdderCounter implements Runnable {
    Counter counter;

    public AdderCounter(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        for (int i = 0; i < COUNT; i++) {
            counter.increment();
        }
    }
}

class SubberCounter implements Runnable {
    Counter counter;

    public SubberCounter(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        for (int i = 0; i < COUNT; i++) {
            counter.decrement();
        }
    }
}

public void test() throws InterruptedException {
    for (int i = 0; i < 10; i++) {
        Counter counter = new Counter();
        AdderCounter ac = new AdderCounter(counter);
        SubberCounter sc = new SubberCounter(counter);

        Thread t1 = new Thread(ac);
        Thread t2 = new Thread(sc);

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(counter.c);
    }
}

Для одного прогона это напечатано:

-33852

28937

-546

-678544

-653775

358351

-593183

1000000 <- Теперь это НАСТОЯЩЕЕ совпадение:) </p>

-826735

-773183

для меня, где вы ожидаете 0, если не будет конфликта нитей.

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

0 голосов
/ 16 мая 2018

Вероятно, худшая вещь об ошибках в параллельных программах - то, что очень часто они не обнаруживаются. Вы можете запускать программу тысячи раз на своем локальном компьютере, и она прекрасно работает. Затем вы перемещаете его на какой-то сервер, к которому получают доступ миллионы людей, и все начинает ломаться «без причины».

0 голосов
/ 16 мая 2018

Совпадение, вот результат на моей машине:

1
0
0

Process finished with exit code 0

И, если вы попробуете еще больше,

for (int i = 0; i < 100; i++) {
    Counter counter = new Counter();
    AdderCounter ac = new AdderCounter(counter);
    SubberCounter sc = new SubberCounter(counter);
    Thread t1 = new Thread(ac);
    Thread t2 = new Thread(sc);

    t1.start();
    t2.start();

    t1.join();
    t2.join();
    System.out.println(counter.c);
    System.out.println();
}

Вы можете получить это:

1
0
0

-1
0
0

1
0
0

1
0
0

1
0
0

-1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

-1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

-1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

-1
0
0

1
0
0

1
0
0

1
0
0

-1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...