Java Thread join () с интервалом ожидания, вызывающим проблему - PullRequest
0 голосов
/ 10 октября 2018

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

Однако, когда я пытаюсь использовать 2 потока дляувеличивает и уменьшает переменную, например, в 100 раз (начальное значение: 2000), конечный результат должен отображать 2000, но он показывает больше 2000 или меньше 2000. Он работает нормально и последовательно показывает 2000, когда я раскомментирую Thread.sleepв основной метод

public class Main {
    public static void main(String[] args) {
        SharedObj sharedObj = new SharedObj();
        Thread thread1 = new Thread(new ThreadMinus(sharedObj));
        Thread thread2 = new Thread(new ThreadPlus(sharedObj));
        thread1.start();
//        try {
//            Thread.sleep(100);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("number is: " + sharedObj.num);
        System.out.println("increment count: " + sharedObj.countPos);
        System.out.println("decrement count: " + sharedObj.countNeg);

    }
}

class SharedObj {
    public int num;
    public int countPos = 0;
    public int countNeg = 0;

    public SharedObj() {
        num = 2000;
    }

    public void change(int x) {
        num += x;
        if (x < 0) {
            countNeg++;
        } else {
            countPos++;
        }
        System.out.println("number is: " + num + " with operation: " + x);
    }
}

class ThreadMinus implements Runnable {
    SharedObj sharedObj;

    public ThreadMinus(SharedObj sharedObj) {
        this.sharedObj = sharedObj;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            sharedObj.change(-1);
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class ThreadPlus implements Runnable {
    SharedObj sharedObj;

    public ThreadPlus(SharedObj sharedObj) {
        this.sharedObj = sharedObj;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            sharedObj.change(+1);
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Ответы [ 4 ]

0 голосов
/ 12 октября 2018

Проблема, которую вы видите, является классическим случаем состояния гонки.Рассмотрим num += x; Для вас это выглядит как одна операция, но на самом деле это несколько операций в процессоре.

Проще говоря, это 1. temp1 = num;2. temp2 = x;3. temp3 = temp1 + temp2 4. num = temp3

Это на самом деле происходит в регистрах ЦП, поэтому вы, очевидно, не видите его при написании Java.

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

Например, давайте начнем с x = 1 и num = 2.

Поток A выполняется:

 1. temp1a = num; 
 2. temp2a = x; 
 3. temp3a = temp1a + temp2a; 
 4. num = temp3a; 

И поток B выполняет

 1. temp1b = num;
 2. temp2b = x;
 3. temp3b = temp1b + temp2b;
 4. num = temp3b;

Теперь, если они выполняют эти операции одновременно, вы можете получить следующий порядок:

 1. temp1a = num; //2
 2. temp2a = x; //1
 3. temp3a = temp1a + temp2a; //3

 4. temp1b = num; //2
 5. temp2b = x; //1
 6. temp3b = temp1b + temp2b; //3
 7. num = temp3b; //3

 8. num = temp3a; //3

Так что, хотя выувеличив num в два раза, вы получите результат 3 вместо 4. Это условие гонки.Есть много способов избежать этого, и синхронизация - один из них, но есть и другие методы.

Если вы хотите лучше понять это, я очень рекомендую этот онлайн-курс.https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY

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

В вашем случае просто добавьте синхронизированную, как это:

public synchronized void change(int x) {
    num += x;
    if (x < 0) {
        countNeg++;
    } else {
        countPos++;
    }
    System.out.println("number is: " + num + " with operation: " + x);
}

решит проблему, но это не волшебная пуля.Вам следует потратить время на то, чтобы понять, почему проблема решается в этом случае, и не решить ее в другом.

Надеюсь, это поможет.

0 голосов
/ 10 октября 2018

join () находится в неправильном положении.

 public class ThreadJoinTest {
    public static void main(String[] args) throws InterruptedException {
        SharedObj sharedObj = new SharedObj();
        Thread thread1 = new Thread(new ThreadMinus(sharedObj));
        Thread thread2 = new Thread(new ThreadPlus(sharedObj));
        thread1.start();
        thread1.join();
//        try {
//            Thread.sleep(100);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        thread2.start();
        thread2.join();
//        try {
//            thread1.join();
//            thread2.join();
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        System.out.println("number is: " + sharedObj.num);
        System.out.println("increment count: " + sharedObj.countPos);
        System.out.println("decrement count: " + sharedObj.countNeg);

    }
}

class SharedObj {
    public int num;
    public int countPos = 0;
    public int countNeg = 0;

    public SharedObj() {
        num = 2000;
    }

    public void change(int x) {
        num += x;
        if (x < 0) {
            countNeg++;
        } else {
            countPos++;
        }
        System.out.println("number is: " + num + " with operation: " + x);
    }
}

class ThreadMinus implements Runnable {
    SharedObj sharedObj;

    public ThreadMinus(SharedObj sharedObj) {
        this.sharedObj = sharedObj;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            sharedObj.change(-1);
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class ThreadPlus implements Runnable {
    SharedObj sharedObj;

    public ThreadPlus(SharedObj sharedObj) {
        this.sharedObj = sharedObj;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            sharedObj.change(+1);
            try {
                Thread.sleep(50);`enter code here`
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
0 голосов
/ 10 октября 2018

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

0 голосов
/ 10 октября 2018

Это не сон / соединение, вызывающее проблемы.Условием гонки является то, что вы увеличиваете ту же самую переменную без ее защиты.Добавьте synchronized в SharedObj # change, и все будет в порядке:

public synchronized void change(int x) { //...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...