2016-08-30 2 views
0

У меня есть два потока, выполняющих вычисления по общей переменной «n», каждый поток увеличивает «n» каждый раз, а другое уменьшает «n» каждый раз, когда я не использую ключевое слово volatile для этой переменной , то я не могу понять, бывает, С.Б. там, пожалуйста, помогите объяснить, фрагмент кода, как следующие:Обмен переменной между потоками в JAVA

public class TwoThreads { 

private static int n = 0; 
private static int called = 0; 

public static void main(String[] args) { 

    for (int i = 0; i < 1000; i++) { 
     n = 0; 
     called = 0; 

     TwoThreads two = new TwoThreads(); 
     Inc inc = two.new Inc(); 
     Dec dec = two.new Dec(); 
     Thread t = new Thread(inc); 
     t.start(); 
     t = new Thread(dec); 
     t.start(); 
     while (called != 2) { 
      //System.out.println("----"); 
     } 
     System.out.println(n); 

    } 
} 

private synchronized void inc() { 
    n++; 
    called++; 
} 

private synchronized void dec() { 
    n--; 
    called++; 
} 

class Inc implements Runnable { 
    @Override 
    public void run() { 
     inc(); 
    } 
} 

class Dec implements Runnable { 
    @Override 
    public void run() { 
     dec(); 
    } 
} 

}

1) Что я ожидаю это «п = 0, называется = 2» после выполнения, но вероятность того, что основной поток может быть заблокирован в цикле while;

2) Но когда я раскомментировать эту строку, программа, когда, как ожидается:

//System.out.println("----"); 

3) Я знаю, что я должен использовать «летучие» на «называется», но я не могу объяснить, почему выше происходит;

4) «called» is «read and load» в рабочей памяти определенного потока, но почему он не «сохраняет и записывает» обратно в основной поток после цикла «long» while, если это не так, почему простой " print "может сделать такую ​​разницу

+1

Я думаю, что вы можете получить тот же эффект, используя метод Thread.sleep() вместо print(). –

+0

@victor вы правы, любая строка может иметь одинаковый эффект, не может объяснить, почему –

+0

Ну, вы не контролируете, когда какой-либо поток выполняется; но основной будет продолжаться, и к тому времени, когда он завершит его, другой поток тоже будет. Вот почему вы должны использовать Thread.join(), чтобы остановить основной, пока остальная часть потока не завершится. (Thread.sleep() будет делать это в течение некоторого времени, но join() будет ждать до финиша) –

ответ

2

У вас синхронизированная запись данных (в inc и dec), но не чтение данных (в основном). ОБА должен быть синхронизирован, чтобы получить предсказуемые эффекты. В противном случае шансы на то, что главный никогда не «видит» изменения, сделанные inc и dec.

+0

спасибо Vasan, но как я могу синхронизировать чтение данных –

+0

Мне нужно будет увидеть весь ваш код для этого, и также есть много разных подходов для этого. В основном, поставьте свой код чтения и записи в синхронизированные блоки и используйте общий объект блокировки. Поскольку main является статическим методом, если вы хотите его прочитать, вы можете использовать только объект класса или статическую переменную уровня класса. Например: 'synchronized (TwoThreads.класс) { '' // чтение п и/или называется here' ''} ' синхронизированы (TwoThreads.class) {' ' // Запись п и/или называется here' ' '} – Vasan

0

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

и попытаться прочитать о гонке данных

while (called != 2) { 
      //System.out.println("----"); 
} 

//.. place for data race, n can be changed 

System.out.println(n); 
+0

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

+0

Вы не можете быть уверены, что в то время как цикл будет заблокирован, используйте для этого функцию call> 2, потому что ваши потоки могут сделать вызов ++ 1000 раз, пока ваш основной поток достигнет цикла while), и вы не можете быть уверены в любом случае, потому что основной поток может достигать цикла while до inc и dec threads make called ++, и условие не удастся, и вы получите еще одну пару новых потоков. –

0

Вам необходимо синхронизировать доступ к called здесь:

while (called != 2) { 
    //System.out.println("----"); 
} 

Я sugest добавить getCalled метод

private synchronized int getCalled() { 
    return called; 
} 

и заменить called != 2 с getCalled() != 2

Если вас интересует, почему эта проблема возникает, вы можете прочитать о видимости в контексте модели Java-памяти.

+0

talex thanks, я буду google, но почему «System.out.println (« ---- ») ;» может выполнить задание, и когда данные будут синхронизированы назад ... –

+0

Поскольку у него есть синхронизация внутри, и эта синхронизация закрашивает некоторые кеши. Нет никакой гарантии, что это не будет изменено в будущем. – talex

Смежные вопросы